UITableViewCell侧滑显示删除按钮后长按复位问题求助
解决UITableViewCell侧滑展开后长按复位的问题
问题出在哪
长按单元格时,系统会触发setHighlighted(_:animated:)或setSelected(_:animated:)方法,默认实现会把单元格的布局/transform重置回初始状态,直接抹掉侧滑展开的状态;另外,系统自带的长按手势可能和你自定义的Pan手势抢事件,也会干扰侧滑状态的维持。
解决步骤
重写单元格的高亮/选中方法,阻止默认布局重置
在自定义TableViewCell里重写setHighlighted(_:animated:)和setSelected(_:animated:),只要单元格处于侧滑展开状态,就跳过系统的默认布局逻辑。调整手势优先级,避免系统长按手势干扰
给自定义Pan手势设置更高优先级,或者如果不需要系统长按功能直接禁用;要是需要保留,就在侧滑展开时暂时让系统长按手势失效。维护侧滑状态标记,确保状态正确传递
在TableViewCell里加一个isSwipedOpen属性标记当前是否展开,所有涉及布局重置的逻辑都先判断这个状态再执行。
代码修改示例
1. 修改TableViewCell类
import UIKit class TableViewCell: UITableViewCell { // 标记当前是否处于侧滑展开状态 var isSwipedOpen = false // 自定义删除按钮 private let deleteButton: UIButton = { let btn = UIButton(type: .custom) btn.backgroundColor = .red btn.setTitle("删除", for: .normal) btn.setTitleColor(.white, for: .normal) return btn }() override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) setupPanGesture() setupDeleteButton() } required init?(coder: NSCoder) { super.init(coder: coder) setupPanGesture() setupDeleteButton() } private func setupPanGesture() { let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:))) // 避免手势抢占问题 panGesture.cancelsTouchesInView = false panGesture.delegate = self addGestureRecognizer(panGesture) } private func setupDeleteButton() { deleteButton.frame = CGRect(x: bounds.width, y: 0, width: 80, height: bounds.height) deleteButton.addTarget(self, action: #selector(deleteTapped), for: .touchUpInside) addSubview(deleteButton) } @objc private func handlePan(_ gesture: UIPanGestureRecognizer) { let translation = gesture.translation(in: self) switch gesture.state { case .changed: if translation.x < 0 { // 向左滑动 let offset = min(-translation.x, 80) // 最大滑动距离匹配删除按钮宽度 transform = CGAffineTransform(translationX: -offset, y: 0) } case .ended, .cancelled: let velocity = gesture.velocity(in: self) let shouldOpen = -translation.x > 40 || velocity.x < -1000 if shouldOpen { UIView.animate(withDuration: 0.2) { self.transform = CGAffineTransform(translationX: -80, y: 0) } isSwipedOpen = true } else { resetCellPosition() } default: break } } @objc private func deleteTapped() { // 这里可以添加删除逻辑,比如通知ViewController处理数据源 resetCellPosition() } func resetCellPosition() { UIView.animate(withDuration: 0.2) { self.transform = .identity } isSwipedOpen = false } // 侧滑展开时不执行默认高亮逻辑 override func setHighlighted(_ highlighted: Bool, animated: Bool) { guard !isSwipedOpen else { return } super.setHighlighted(highlighted, animated: animated) } // 侧滑展开时不执行默认选中逻辑 override func setSelected(_ selected: Bool, animated: Bool) { guard !isSwipedOpen else { return } super.setSelected(selected, animated: animated) } // 处理手势冲突,允许和系统长按手势同时识别,但通过状态阻止复位 override func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { return otherGestureRecognizer is UILongPressGestureRecognizer } }
2. 修改ViewController类
import UIKit class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { private let tableView = UITableView(frame: .zero, style: .plain) private var data = ["Item 1", "Item 2", "Item 3", "Item 4", "Item 5"] override func viewDidLoad() { super.viewDidLoad() setupTableView() } private func setupTableView() { tableView.dataSource = self tableView.delegate = self tableView.register(TableViewCell.self, forCellReuseIdentifier: "Cell") tableView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(tableView) NSLayoutConstraint.activate([ tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor), tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor), tableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor) ]) } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return data.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! TableViewCell cell.textLabel?.text = data[indexPath.row] // 复用单元格时复位侧滑状态 if cell.isSwipedOpen { cell.resetCellPosition() } return cell } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { // 选中单元格时,复位其他已展开的单元格 for cell in tableView.visibleCells { guard let customCell = cell as? TableViewCell else { continue } if customCell.isSwipedOpen { customCell.resetCellPosition() } } tableView.deselectRow(at: indexPath, animated: true) } // 如果不需要系统的长按编辑功能,直接禁用 override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { return false } }
额外注意点
- 如果需要保留系统长按功能(比如长按弹出菜单),可以添加以下方法,在侧滑展开时阻止系统长按手势触发:
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { if gestureRecognizer is UILongPressGestureRecognizer && isSwipedOpen { return false } return super.gestureRecognizerShouldBegin(gestureRecognizer) }
内容的提问来源于stack exchange,提问作者Mete Vesek
相关产品推荐
相关产品推荐

