如何向 TableView 添加向左滑动手势和 return 滑动时的单元格位置 Swift 3

How to add a Swipe Left gesture to a TableView and return the cell location when swiped in Swift 3

我有一个 TableView,它已经编码为在使用 didSelectRowAt 方法选择任何单元格时执行操作。

现在,我想向 Table(或单元格)添加向左滑动手势,这样我就可以在滑动单元格而不是点击时执行辅助操作。

1) 我希望单元格在滑动时向左移动,但我不想在单元格移动的 space 中添加按钮。

2) 相反,我希望能够 'drag' 单元格离开到某个点(比如中途),然后使用 indexPath 执行辅助操作(所以我知道哪个单元格被拖走了)。

3) 如果用户停止拖动或放开单元格,我希望它 return 到它的起始位置并且没有任何动作发生。

我已经看到很多示例执行此操作的各个部分,但大多数示例都在 Obj-C 中或在与单元格相同的行中插入按钮。

还有,是不是每个单元格都加上Gesture比较好? 将它添加到 table...

似乎更聪明

编辑:请参阅下面的完整答案代码

我做了一些研究并创建了一个简单的例子来说明如何做到这一点 - 创建一个可以滑动和点击的 table 单元格。我在音乐播放器中使用它 - 点击一个单元格并播放歌曲,滑动相同的单元格并转到不同的视图。

我已经根据这两个现有示例构建了我的解决方案: https://www.raywenderlich.com/77974/making-a-gesture-driven-to-do-list-app-like-clear-in-swift-part-1

https://gabrielghe.github.io/swift/2016/03/20/swipable-uitableviewcell

我没有要共享的存储库,所以所有代码都在这里。

我正在使用 Xcode 8.2.1 和 Swift 3

第 1 步:

  • 创建一个新的单视图 Swift 项目,然后打开 Storyboard。
  • 将一个 TableView 拖到现有的 ViewController 上并拖动它以适合视图。

第 2 步:

  • 将新的 Swift 文件添加到项目并将其命名为 "TableViewCellSliding.swift"。
  • Copy/paste将下面的代码放入新文件中。

    //
    //  TableViewCellSliding.swift
    //
    
    import UIKit
    
    protocol SlidingCellDelegate {
        // tell the TableView that a swipe happened
        func hasPerformedSwipe(touch: CGPoint)
        func hasPerformedTap(touch: CGPoint)
    }
    
    class SlidingTableViewCell: UITableViewCell {
        var delegate: SlidingCellDelegate?
        var originalCenter = CGPoint()
        var isSwipeSuccessful = false
        var touch = CGPoint()
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
    
        // add a PAN gesture
        let pRecognizer = UIPanGestureRecognizer(target: self, action: #selector(SlidingTableViewCell.handlePan(_:)))
        pRecognizer.delegate = self
        addGestureRecognizer(pRecognizer)
    
        // add a TAP gesture
        // note that adding the PAN gesture to a cell disables the built-in tap responder (didSelectRowAtIndexPath)
        // so we can add in our own here if we want both swipe and tap actions
        let tRecognizer = UITapGestureRecognizer(target: self, action: #selector(SlidingTableViewCell.handleTap(_:)))
        tRecognizer.delegate = self
        addGestureRecognizer(tRecognizer)
    }
    
    override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        if let panGestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer {
            let translation = panGestureRecognizer.translation(in: superview!)
            //look for right-swipe
            if (fabs(translation.x) > fabs(translation.y)) && (translation.x > 0){
    
            // look for left-swipe
            //if (fabs(translation.x) > fabs(translation.y)) && (translation.x < 0){
                //print("gesture 1")
                touch = panGestureRecognizer.location(in: superview)
                return true
            }
            //not left or right - must be up or down
            return false
        }else if gestureRecognizer is UITapGestureRecognizer {
            touch = gestureRecognizer.location(in: superview)
            return true
        }
        return false
    }
    
    func handleTap(_ recognizer: UITapGestureRecognizer){
        // call function to get indexPath since didSelectRowAtIndexPath will be disabled
        delegate?.hasPerformedTap(touch: touch)
    }
    
    func handlePan(_ recognizer: UIPanGestureRecognizer) {
        if recognizer.state == .began {
            originalCenter = center
        }
    
        if recognizer.state == .changed {
            checkIfSwiped(recongizer: recognizer)
        }
    
        if recognizer.state == .ended {
            let originalFrame = CGRect(x: 0, y: frame.origin.y, width: bounds.size.width, height: bounds.size.height)
            if isSwipeSuccessful{
                delegate?.hasPerformedSwipe(touch: touch)
    
                //after 'short' swipe animate back to origin quickly
                moveViewBackIntoPlaceSlowly(originalFrame: originalFrame)
            } else {
                //after successful swipe animate back to origin slowly
                moveViewBackIntoPlace(originalFrame: originalFrame)
            }
        }
    }
    
    func checkIfSwiped(recongizer: UIPanGestureRecognizer) {
        let translation = recongizer.translation(in: self)
        center = CGPoint(x: originalCenter.x + translation.x, y: originalCenter.y)
    
        //this allows only swipe-right
        isSwipeSuccessful = frame.origin.x > frame.size.width / 2.0  //pan is 1/2 width of the cell
    
        //this allows only swipe-left
        //isSwipeSuccessful = frame.origin.x < -frame.size.width / 3.0  //pan is 1/3 width of the cell
    }
    
    func moveViewBackIntoPlace(originalFrame: CGRect) {
        UIView.animate(withDuration: 0.2, animations: {self.frame = originalFrame})
    }
    func moveViewBackIntoPlaceSlowly(originalFrame: CGRect) {
        UIView.animate(withDuration: 1.5, animations: {self.frame = originalFrame})
    }
    
    }
    

第 3 步:

  • Copy/paste将下面的代码放入现有文件"ViewController.swift"

    //
    //  ViewController.swift
    //
    
    import UIKit
    
    class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, SlidingCellDelegate {
    
    @IBOutlet weak var tableView: UITableView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    
        tableView.dataSource = self
        tableView.delegate = self
        tableView.register(SlidingTableViewCell.self, forCellReuseIdentifier: "cell")
        tableView.rowHeight = 50;
    }
    
    func hasPerformedSwipe(touch: CGPoint) {
        if let indexPath = tableView.indexPathForRow(at: touch) {
            // Access the image or the cell at this index path
            print("got a swipe row:\(indexPath.row)")
        }
    }
    
    func hasPerformedTap(touch: CGPoint){
        if let indexPath = tableView.indexPathForRow(at: touch) {
        // Access the image or the cell at this index path
            print("got a tap row:\(indexPath.row)")
        }
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }
    func tableView(_ tableView: UITableView,numberOfRowsInSection section: Int)-> Int {
        return 100
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell=tableView.dequeueReusableCell(withIdentifier: "cell",for: indexPath) as! SlidingTableViewCell
        // Configure cell
        cell.selectionStyle = .none
        cell.textLabel?.text = "hello \(indexPath.row)"
        cell.delegate = self
        return cell
    }
    func tableView(sender: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        // do stuff with indexPath.row and indexPath.section
        //never make it here because we added a tap gesture but this will 
        print("selected cell")
    }
    }
    

第 4 步:

在情节提要中将其全部连接起来。

  • 打开 Storyboard 视图和 select TableView。转到 Connections Inspector(圆圈中的右上角箭头)并从 New Referencing Outlet 拖到 TableView 并从弹出菜单中 select "tableView"。

  • 在 TableView 仍然 selected 的情况下,从 Outlets > dataSource 拖动到 Storyboard 中的 TableView。从 Outlets > delegate.

  • 开始重复

第 5 步:

  • 运行啦!

我不会详细介绍任何代码,因为顶部的两个链接做得很好。这只是关于拥有可以构建的完整、简单、干净的代码。享受吧。