自定义 UITableView 重新排序

Posted

技术标签:

【中文标题】自定义 UITableView 重新排序【英文标题】:Custom UITableView reorder 【发布时间】:2010-10-25 00:24:38 【问题描述】:

我想知道是否有人有一个好的教程的链接,或者可以指出我正确的方向,以便在 Epic Win App 等 UITableView 中重新创建“拖动以重新排序”单元格。基本想法是您点击并按住列表项,然后拖动到您希望该项目所在的位置。任何帮助将不胜感激。

【问题讨论】:

如何实现这一点,如果我们有多个部分,每个部分有一行????请帮帮我! 【参考方案1】:

使用内置方法的最简单方法如下:

首先,将表格单元格设置为启用显示重新排序控制。最简单的例子(使用 ARC):

这是假设 NSMutableArray *things 已在此类的其他位置创建

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
    UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"do not leak me"];
    if (!cell) 
        cell = [[UITableViewCell alloc] init];
        cell.showsReorderControl = YES;
    
    cell.textLabel.text = [things objectAtIndex:indexPath.row];
    return cell;

实现您的两个UITableViewDelegate 方法,如下所示:

这个方法告诉tableView允许重新排序

-(BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath 
    return YES;

这是 tableView 重新排序实际发生的地方

-(void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath 
    id thing = [things objectAtIndex:sourceIndexPath.row];
    [things removeObjectAtIndex:sourceIndexPath.row];
    [things insertObject:thing atIndex:destinationIndexPath.row];


然后不知何故,在某个地方,将编辑设置为真。这是您如何执行此操作的一个示例。 tableView 必须处于编辑模式才能重新排序。

- (IBAction)doSomething:(id)sender 
    self.table.editing = YES;

希望作为一个具体的例子。

【讨论】:

该问题明确指出要提供自定义重新排序选项,而不是默认选项。 @Unome 否。OP 选择了已接受的答案,这基本上是我答案的更摘要版本。谢谢。 像你说的那样接受的答案是问题要求的淡化版本。我已经发布了 UITableView 的工作子类。 我很抱歉。回想起来,我的cmets很粗鲁。我很沮丧,因为答案没有为我作为用户寻找的内容提供帮助。您的问题写得很仔细,写得很好,并提供了一个很好的答案,尽管不是我想要的答案。 +1【参考方案2】:

这很简单——这可能就是为什么没有关于这个问题的明确教程的原因。

正常创建UITableView即可,但是将每个单元格的showsReorderControl设置为TRUE。当您进入编辑模式时(通常通过按下“编辑”按钮并将 UITableView 的“编辑”值设置为 TRUE) - 重新排序栏将出现在单元格中。

注意:

如果您的数据源实现 tableView:canMoveRowAtIndexPath: - 并且它返回“NO” - 重新排序栏将不会出现。

您还需要在数据源委托中实现–tableView:moveRowAtIndexPath:toIndexPath:

【讨论】:

我想做的一件事就是取代按钮并使整个单元格成为按钮。因此,您只需拖动单元格即可。你知道怎么做吗?还是如何做到这一点的垫脚石? 将 UITableCellView 子类化为您想要的任何东西都非常容易 - 因此只需使用您自己的重新排序栏按钮/区域来制作您的子类。 您是否能够按照 Brad 的建议重新连接栏/按钮区域?据我所见,拦截或调整重新排序按钮区域的大小绝非易事。看起来您可以在这里使用 Apple 的整个方法,或者完全从头开始重新排序。 我同意 Thomson 的观点,这远不是一个简单的问题,而且比使用几个 tableView 委托方法和编写子类要深入得多。如果您声称某事很容易,请提供证明它很容易的证据。【参考方案3】:

此解决方案的功能类似于 OSX 上的提醒应用程序,它允许您通过按住并拖动任意位置来重新排列单元格。

我创建了一个 UITableView 的子类,它允许您拖动和重新排序单元格,而无需打开 editing 或使用标准的重新排序句柄。

基本的想法是我使用触摸开始和触摸结束来获取点击了哪个单元格,然后我创建了一个假视图,它实际上只是您点击的视图的屏幕截图,并来回移动它,交换单元格的背景。当用户放手时,我会取消隐藏原始视图。

class ReorderTableView: UITableView 

var customView:UIImageView?
var oldIndexPath:NSIndexPath?

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) 
    guard let touch1 = touches.first else
        return
    

    oldIndexPath = self.indexPathForRowAtPoint(touch1.locationInView(self))
    guard (oldIndexPath != nil) else
        return
    
    let oldCell = self.cellForRowAtIndexPath(self.oldIndexPath!)

    customView = UIImageView(frame: CGRectMake(0, touch1.locationInView(self).y - 20, self.frame.width, 40))
    customView?.image = screenShotView(oldCell!)
    customView?.layer.shadowColor = UIColor.blackColor().CGColor
    customView?.layer.shadowOpacity = 0.5
    customView?.layer.shadowOffset = CGSizeMake(1, 1)

    self.addSubview(customView!)
    oldCell?.alpha = 0


override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) 

    guard let touch1 = touches.first else
        return
    
    let newIndexPath = self.indexPathForRowAtPoint(touch1.locationInView(self))
    guard newIndexPath != nil else
        return
    
    guard oldIndexPath != nil else
        return
    
    if newIndexPath != oldIndexPath
        self.moveRowAtIndexPath(oldIndexPath!, toIndexPath: newIndexPath!)
        oldIndexPath = newIndexPath
        self.cellForRowAtIndexPath(self.oldIndexPath!)!.alpha = 0
    
    self.customView!.frame.origin = CGPointMake(0, touch1.locationInView(self).y - 20)


override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) 
    self.customView?.removeFromSuperview()
    self.customView = nil
    guard (oldIndexPath != nil) else
        return
    
    self.cellForRowAtIndexPath(self.oldIndexPath!)!.alpha = 1


func screenShotView(view: UIView) -> UIImage? 

    let rect = view.bounds
    UIGraphicsBeginImageContextWithOptions(rect.size,true,0.0)
    let context = UIGraphicsGetCurrentContext()

    CGContextTranslateCTM(context, 0, -view.frame.origin.y);
    self.layer.renderInContext(context!)
    let capturedImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return capturedImage



最后,您可以重新排序单元格,而无需使用句柄。这可以针对不同的手势或模式进行扩展,具体取决于用例,但我将其保留为香草版本。如果您有任何问题,请告诉我。

更新:

我发现了一些注意事项。弄清楚如何使用滚动并相应地修改它是非常重要的。例如,当我模拟它时,我设置了scrollingEnabled = false. 从长远来看,我确实想要滚动,所以我将代码更改为仅在超过 0.25 的压力机上运行此功能(我使用了一些计时器来执行此操作)。然后我会在此期间暂时禁用滚动,并在完成后重新启用。

【讨论】:

是什么让你认为 OP 想要这个?接受的答案使用句柄。谢谢。 他的问题:“基本的想法是你点击并按住一个列表项,然后你拖动到你想要的项目”没有指定他想使用句柄,并且标题“自定义重新排序”意味着不使用默认方法。我有一个类似的问题,这里的答案都没有帮助我。我试图提供一个答案,以帮助那些有类似问题的人。接受的答案不等于正确答案。谢谢。 一切都好。如果您想添加另一个答案,以显示另一种完美的方法。无需围绕其他答案来宣传您的 +1 我明白,我的 cmets 是在尝试回答问题之前制作的,我的意图绝不是宣传我的答案。 一切都好。那么这个答案中的20 之类的硬编码数字是什么?

以上是关于自定义 UITableView 重新排序的主要内容,如果未能解决你的问题,请参考以下文章

UITableViewCell 自定义重新排序控件

数据未显示在 UITableView 的自定义单元格中

更改 UITableView 中重新排序图标的颜色

重新排序单元格后重新加载自定义 UITableViewCell

UITableView 中的自定义绘图在 5 个单元格后失败

进入编辑/重新排序模式时的 UITableViewCell 自定义绘图和动画