在没有重新排序控制的情况下重新排序 UITableView

Posted

技术标签:

【中文标题】在没有重新排序控制的情况下重新排序 UITableView【英文标题】:Reordering UITableView without reorder control 【发布时间】:2011-08-03 05:48:51 【问题描述】:

我需要用户能够通过这种方式重新排序 UITableView:他触摸一个单元格一段预定的时间(例如 1 秒),然后他可以将其拖放到其他单元格上。

我知道如何使用手势识别器实现“长触摸”检测,但是在不使用重新排序控件的情况下实现拖放功能的最佳方法是什么(用户应该从单元格中的任何位置拖动单元格,不仅来自重新排序控件)?

【问题讨论】:

你已经解决了这个问题吗? 【参考方案1】:

这是一个老问题,但这里有一个经过测试并适用于 iOS 811 的解决方案。

在你的 UITableViewCell 子类中试试这个:

class MyTableViewCell: UITableViewCell 
    weak var reorderControl: UIView?

    override func layoutSubviews() 
        super.layoutSubviews()

        // Make the cell's `contentView` as big as the entire cell.
        contentView.frame = bounds

        // Make the reorder control as big as the entire cell 
        // so you can drag from everywhere inside the cell.
        reorderControl?.frame = bounds
    

    override func setEditing(_ editing: Bool, animated: Bool) 
        super.setEditing(editing, animated: false)
        if !editing || reorderControl != nil 
            return
        

        // Find the reorder control in the cell's subviews.
        for view in subviews 
            let className = String(describing: type(of:view))
            if className == "UITableViewCellReorderControl" 

                // Remove its subviews so that they don't mess up
                // your own content's appearance.
                for subview in view.subviews 
                    subview.removeFromSuperview()
                

                // Keep a weak reference to it for `layoutSubviews()`.
                reorderControl = view

                break
            
        
    

它接近Senseful's first suggestion,但他引用的文章似乎不再有效。

您所做的是使重新排序控件和单元格的内容视图在编辑时与整个单元格一样大。这样您就可以从单元格内的任何位置拖动,并且您的内容会占据整个空间,就好像单元格根本没有被编辑一样。

最重要的缺点是您正在更改系统的单元格视图结构并引用私有类 (UITableViewCellReorderControl)。它似乎适用于所有最新的 ios 版本,但您必须确保每次新操作系统出现时它仍然有效。

【讨论】:

为什么 super.setEditing 动画设置为 false?这有什么区别? 虽然我完全欣赏这个答案的相对简单性并且我尝试过......似乎通过将重新排序控件的大小增加到整个单元格,您无意中干扰了滚动检测(如果您的单元格是在滚动视图中)......它几乎总是假设您正在重新排序而不是滚动......点击似乎最终会调用 onRowMoved() 而不是自定义单元格的任何自定义(可点击)子视图的点击事件。似乎苹果选择了一个小型但立即响应的重新排序控件,而不是像 android 那样在单元格范围内长按。 我将动画设置为 false,因为在我的情况下,动画在我删除系统的重新排序控制之后改变了视图结构。关于滚动检测,虽然我没有遇到类似的挑战,但也许您可以更改系统手势识别器的配置以改进滚动检测并保持重新排序控制的优势。如果您有一些代码要分享,也许我们也可以提供帮助:)【参考方案2】:

我解决了以下步骤的问题:

    将手势识别器附加到 UITableView。 检测“长按”轻敲了哪个单元格。此时创建所选单元格的快照,将其放入 UIImageView 并将其放置在 UITableView 上。 UIImageView 的坐标应该相对于 UITableView 计算所选单元格(所选单元格的快照应该覆盖所选单元格)。 存储所选单元格的索引,删除所选单元格并重新加载 UITableView。 禁用 UITableView 的滚动。现在您需要在拖动单元格时更改快照 UIImageView 的框架。您可以在touchesMoved 方法中执行此操作。 当用户手指离开屏幕时,创建新单元格并重新加载 UITableView(您已经存储了索引)。 删除快照 UIImageView。

但这并不容易。

【讨论】:

为什么必须执行第 4 步(禁用滚动)? @LancelotdelaMare 当一个部分只有一行并且我将该行拖出该部分时,我无法将其拖回该部分;即使是中间阻力,也不会建议我将其放回刚刚取出的部分。它似乎不像用户期望的那样工作,您可以在示例项目中复制它。 @KrisGellci:你是对的,这是有问题的。我通过禁用所有单元格的删除在我的客户端解决了这个问题(我在每个部分都有一个零高度单元格)。肯定有更好的方法。请提交针对 BVReorderTableView 的错误。【参考方案3】:

文章Reordering a UITableViewCell from any touch point 讨论了这个确切的场景。

基本上你会做以下事情:

    找到UITableViewCellReorderControl(私有类)。 将其展开,使其跨越整个单元格。 隐藏它。 用户现在可以从任意位置拖动单元格。

另一个解决方案,Cookbook: Moving Table View Cells with a Long Press Gesture,通过执行以下操作达到相同的效果:

    在表格视图上添加长按手势识别器。 拖动单元格时创建单元格快照。 拖动单元格时,四处移动快照,然后调用-[UITableView moveRowAtIndexPath:toIndexPath:]。 手势结束后,隐藏单元格快照。

【讨论】:

【参考方案4】:

供将来参考... 我有同样的问题,我发现了另一个关于它的问题(Swift - Drag And Drop TableViewCell with Long Gesture Recognizer),有人建议这个教程:https://www.freshconsulting.com/create-drag-and-drop-uitableview-swift/ 非常适合我

【讨论】:

【参考方案5】:

我知道这是一个关于 UITableView 的问题。但我以使用 UICollectionView 而不是 UITableView 来实现 longtap 重新排序的解决方案结束。它简单易行。

【讨论】:

【参考方案6】:
tableView.dragInteractionEnabled = true
tableView.dragDelegate = self
tableView.dropDelegate = self


func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath)  

func tableView(_ tableView: UITableView, itemsForBeginning session: UIDragSession,
                    at: indexPath: IndexPath) -> [UIDragItem] 
    return [UIDragItem(itemProvider: NSItemProvider())]



func tableView(_ tableView: UITableView, dropSessionDidUpdate session: 
    UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UITableViewDropProposal 

    if session.localDragSession != nil  
        return UITableViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)
    
    return UITableViewDropProposal(operation: .cancel, intent: .unspecified)


func tableView(_ tableView: UITableView, performDropWith coordinator: UITableViewDropCoordinator) 

【讨论】:

以上是关于在没有重新排序控制的情况下重新排序 UITableView的主要内容,如果未能解决你的问题,请参考以下文章

在提交表单之前重新排序输入

UITable 视图界面构建器创建

在不更改索引的情况下重新排序数组

在不更改值顺序的情况下重新排序因子的级别

平均 UDP 数据包丢失和数据包重新排序

AG-Grid - 显示排序图标而不重新加载数据