Swift - 使用长按手势识别器拖放TableView单元格

7
我看到了很多有关重新排序单元格的帖子,这些帖子涉及使用“编辑模式”,但没有针对我遇到的问题的帖子。(如果我错了,请原谅我)。
我正在构建一个排名应用程序,并寻找一种使用长手势识别器来重新排列UITableView中的单元格的方法。基本上,用户将能够与朋友一起重新排序和“排名”字符串组中的单元格。
我会按照标准路线在导航栏中使用“编辑”栏按钮项,但我已经在导航栏的右上角添加了用于向tableview添加新字符串的选项。(以下图像描述了我的意思)。
到目前为止,我已经添加了`

`
    var lpgr = UILongPressGestureRecognizer(target: self, action: "longPressDetected:")

    lpgr.minimumPressDuration = 1.0;
    tableView.addGestureRecognizer(lpgr)`

我在viewDidLoad方法中创建了以下函数:

    func longPressDetected(sender: AnyObject) {

    var longPress:UILongPressGestureRecognizer = sender as UILongPressGestureRecognizer
    var state:UIGestureRecognizerState = longPress.state

    let location:CGPoint = longPress.locationInView(self.tableView) as CGPoint
    var indexPath = self.tableView.indexPathForRowAtPoint(location)?

    var snapshot:UIView!
    var sourceIndexPath:NSIndexPath!

}

我在互联网上搜索的所有资源最终都显示出一个巨大而冗长的添加功能列表,以便获得所需的结果,但这些示例涉及核心数据。我认为必须有一种更简单的方法来通过长按重新排序tableview单元格。请参考下图: enter image description here

是的,你绝对不需要使用核心数据。你只是想拖放重新排序单元格吗?在手势识别时将表视图单元格复制为可拖动视图,然后一旦手势结束,删除该视图并相应地更新表内容。 - Lyndsey Scott
可能是在tableview中使用长按手势重新排序单元格?的重复问题。 - Grant Kamin
3个回答

23

Dave的回答很好。这是这个教程的Swift 4版本:

WayPointCell是你的CustomUITableViewCellwayPoints是UITableView的数据源数组。

首先,像Alfi提到的那样,在你的viewDidLoad中放入以下内容:

override func viewDidLoad() {
    super.viewDidLoad()

    let longpress = UILongPressGestureRecognizer(target: self, action: #selector(longPressGestureRecognized(gestureRecognizer:)))
    self.tableView.addGestureRecognizer(longpress)
}

然后实现该方法:

func longPressGestureRecognized(gestureRecognizer: UIGestureRecognizer) {

    let longpress = gestureRecognizer as! UILongPressGestureRecognizer
    let state = longpress.state
    let locationInView = longpress.location(in: self.tableView)
    var indexPath = self.tableView.indexPathForRow(at: locationInView)

    switch state {
    case .began:
        if indexPath != nil {
            Path.initialIndexPath = indexPath
            let cell = self.tableView.cellForRow(at: indexPath!) as! WayPointCell
            My.cellSnapShot = snapshopOfCell(inputView: cell)
            var center = cell.center
            My.cellSnapShot?.center = center
            My.cellSnapShot?.alpha = 0.0
            self.tableView.addSubview(My.cellSnapShot!)

            UIView.animate(withDuration: 0.25, animations: {
                center.y = locationInView.y
                My.cellSnapShot?.center = center
                My.cellSnapShot?.transform = CGAffineTransform(scaleX: 1.05, y: 1.05)
                My.cellSnapShot?.alpha = 0.98
                cell.alpha = 0.0
            }, completion: { (finished) -> Void in
                if finished {
                    cell.isHidden = true
                }
            })
        }

    case .changed:
        var center = My.cellSnapShot?.center
        center?.y = locationInView.y
        My.cellSnapShot?.center = center!
        if ((indexPath != nil) && (indexPath != Path.initialIndexPath)) {

            self.wayPoints.swapAt((indexPath?.row)!, (Path.initialIndexPath?.row)!)
            //swap(&self.wayPoints[(indexPath?.row)!], &self.wayPoints[(Path.initialIndexPath?.row)!])
            self.tableView.moveRow(at: Path.initialIndexPath!, to: indexPath!)
            Path.initialIndexPath = indexPath
        }

    default:
        let cell = self.tableView.cellForRow(at: Path.initialIndexPath!) as! WayPointCell
        cell.isHidden = false
        cell.alpha = 0.0
        UIView.animate(withDuration: 0.25, animations: {
            My.cellSnapShot?.center = cell.center
            My.cellSnapShot?.transform = .identity
            My.cellSnapShot?.alpha = 0.0
            cell.alpha = 1.0
        }, completion: { (finished) -> Void in
            if finished {
                Path.initialIndexPath = nil
                My.cellSnapShot?.removeFromSuperview()
                My.cellSnapShot = nil
            }
        })
    }
}

func snapshopOfCell(inputView: UIView) -> UIView {

    UIGraphicsBeginImageContextWithOptions(inputView.bounds.size, false, 0.0)
    inputView.layer.render(in: UIGraphicsGetCurrentContext()!)
    let image = UIGraphicsGetImageFromCurrentImageContext()!
    UIGraphicsEndImageContext()
    let cellSnapshot : UIView = UIImageView(image: image)
    cellSnapshot.layer.masksToBounds = false
    cellSnapshot.layer.cornerRadius = 0.0
    cellSnapshot.layer.shadowOffset = CGSize(width: -5.0, height: 0.0)
    cellSnapshot.layer.shadowRadius = 5.0
    cellSnapshot.layer.shadowOpacity = 0.4
    return cellSnapshot
}

struct My {
    static var cellSnapShot: UIView? = nil
}

struct Path {
    static var initialIndexPath: IndexPath? = nil
}

1
不要忘记在ViewDidLoad中添加这两行代码:let longpress = UILongPressGestureRecognizer(target: self, action: "longPressGestureRecognized:") tableView.addGestureRecognizer(longpress) - Ahmadreza
4
当长按手势到达屏幕底部时,如何滚动tableView? - alionthego
1
@CliftonLabrum 请查看此SO帖子以了解该问题:https://dev59.com/FI7ea4cB1Zd3GeqPCYnU - Teetz
1
这个解决方案有效!但是我遇到了崩溃:终止应用程序,因为未捕获的异常'NSInternalInconsistencyException',原因是:无效的更新:在第0个部分中的行数无效。更新后现有部分中包含的行数(9)必须等于更新前该部分中包含的行数(9),加上或减去从该部分中插入或删除的行数(0插入,0删除),以及加上或减去移动到该部分中或移出该部分的行数(0移入,1移出)。当将单元格移动到另一个tableview部分时,我该如何解决这个问题? - fphelp
1
@alionthego 我已经在展开/折叠的UITableview中开发了同样的东西,但是我无法在UITable的末尾添加滚动条,该怎么做呢? - A.s.ALI
显示剩余6条评论

9

尝试这个教程,你可能在20分钟内开始若干操作:

Swift拖放教程

十分简单。我才学习开发三个月,就可以实现它。我也尝试了其他几种方法,但只有这种我能看懂。

它是用Swift编写的,而且剪贴复制非常方便。你只需要将longPress代码添加到viewDidLoad中,然后将函数粘贴到类的“body”中。教程会给你指导,但没有太多复杂的地方。

代码快速解释:此方法使用switch语句来检测长按是否刚开始、更改或处于默认状态。不同的代码会运行。它会拍下/截图长按的单元格,隐藏你的单元格并移动该截图。完成后,它会显示你的单元格,并从视图中删除该截图。

警告:我的一个警告是,尽管这种拖放看起来很好,并且几乎完美地工作,但似乎存在一个问题,即当你将单元格拖到最低/底部单元格以下时,它会崩溃。

拖放崩溃问题


3
这是我看过的第一个基本上就“奏效”的教程。感谢分享! - Andrew Varvel
谢谢!它可以了。问题:我为longPressEvent添加了一个状态“ended”,然后它就停止工作了...就像cellSnapShot重叠在其他行上等。基本上,我试图在长按手势结束时更新我的数据库。 - Mohit Kumar
你能帮忙解决这个问题吗:https://dev59.com/GLv4oIgBc1ULPQZFjVy4#72265135 - Kishan Bhatiya
为什么你在使用 My 和 Path 等结构体,如果没有它们为什么不能正常工作? - Alexander Yakovlev

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接