You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

UITableViewCell侧滑显示删除按钮后长按复位问题求助

解决UITableViewCell侧滑展开后长按复位的问题

问题出在哪

长按单元格时,系统会触发setHighlighted(_:animated:)或setSelected(_:animated:)方法,默认实现会把单元格的布局/transform重置回初始状态,直接抹掉侧滑展开的状态;另外,系统自带的长按手势可能和你自定义的Pan手势抢事件,也会干扰侧滑状态的维持。

解决步骤

  1. 重写单元格的高亮/选中方法,阻止默认布局重置
    在自定义TableViewCell里重写setHighlighted(_:animated:)和setSelected(_:animated:),只要单元格处于侧滑展开状态,就跳过系统的默认布局逻辑。

  2. 调整手势优先级,避免系统长按手势干扰
    给自定义Pan手势设置更高优先级,或者如果不需要系统长按功能直接禁用;要是需要保留,就在侧滑展开时暂时让系统长按手势失效。

  3. 维护侧滑状态标记,确保状态正确传递
    在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 04:57:31