UITableView 以编程方式调用滑动操作

Posted

技术标签:

【中文标题】UITableView 以编程方式调用滑动操作【英文标题】:UITableView invoke swipe actions programmatically 【发布时间】:2014-11-05 20:13:57 【问题描述】:

我有一个 UITableView,用户可以在其中向左滑动以显示操作(例如在 ios 8 邮件中)。这一切都按预期工作。我想在用户点击单元格的某个部分时触发它。如何以编程方式调用此幻灯片操作?

当前行为:用户必须向左滑动单元格才能显示操作按钮。

期望的行为:用户在单元格上点击(操作按钮)。单元格滑过以显示操作按钮。

【问题讨论】:

除了下面的解决方法之外,您是否找到了一种编程方式来执行此操作?谢谢 我没有。我认为目前没有针对此的公共 API。也许是 iOS 9? 有什么方法可以在不滑动的情况下显示 UITableViewRowAction 而是以编程方式显示用户交互? 【参考方案1】:

好吧,我找不到以编程方式执行此操作的方法,但我想出了这个解决方法。当用户点击单元格时,我将其动画(平移)到左侧以暂时显示虚假的“Swipe Me”按钮。这很快被逆转,因此细胞恢复正常。这提供了一个视觉提示,让用户知道他们可以滑动单元格:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];

    __block UILabel *swipeLabel = [[UILabel alloc]initWithFrame:CGRectMake(cell.bounds.size.width,
                                                                   0,
                                                                   200,
                                                                   cell.bounds.size.height)];

    swipeLabel.text = @"  Swipe Me";
    swipeLabel.backgroundColor = [UIColor greenColor];
    swipeLabel.textColor = [UIColor whiteColor];
    [cell addSubview:swipeLabel];

    [UIView animateWithDuration:0.3 animations:^
        [cell setFrame:CGRectMake(cell.frame.origin.x - 100, cell.frame.origin.y, cell.bounds.size.width, cell.bounds.size.height)];
     completion:^(BOOL finished) 
        [UIView animateWithDuration:0.3 animations:^
            [cell setFrame:CGRectMake(cell.frame.origin.x + 100, cell.frame.origin.y, cell.bounds.size.width, cell.bounds.size.height)];
         completion:^(BOOL finished) 
            [swipeLabel removeFromSuperview];
            swipeLabel = nil;
        ];
    ];

希望这对某人有所帮助。

请注意,您需要将 tableViewCell 的选择类型设置为无。否则灰色条会遮住它。

更新。我想我会发布一个更 Swifty 的版本:

func previewActions(forCellAt indexPath: IndexPath) 
    guard let cell = tableView.cellForRow(at: indexPath) else 
        return
    

    let label: UILabel = 
        let label = UILabel(frame: CGRect.zero)
        label.text = "  Swipe Me  "
        label.backgroundColor = .blue
        label.textColor = .white
        return label
    ()

    // Figure out the best width and update label.frame
    let bestSize = label.sizeThatFits(label.frame.size)
    label.frame = CGRect(x: cell.bounds.width - bestSize.width, y: 0, width: bestSize.width, height: cell.bounds.height)
    cell.insertSubview(label, belowSubview: cell.contentView)

    UIView.animate(withDuration: 0.3, animations: 
        cell.transform = CGAffineTransform.identity.translatedBy(x: -label.bounds.width, y: 0)
        label.transform = CGAffineTransform.identity.translatedBy(x: label.bounds.width, y: 0)
    )  (finished) in
        UIView.animateKeyframes(withDuration: 0.3, delay: 0.25, options: [], animations: 
            cell.transform = CGAffineTransform.identity
            label.transform = CGAffineTransform.identity
        , completion:  (finished) in
            label.removeFromSuperview()
        )
    


func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) 
    previewActions(forCellAt: indexPath)
    return

【讨论】:

太棒了。对于第二部分,我使用了延迟,因此文本会在一瞬间出现[UIView animateWithDuration:0.5 delay:0.5 options:0 animations:^ [cell setFrame:CGRectMake(cell.frame.origin.x + 100, cell.frame.origin.y, cell.bounds.size.width, cell.bounds.size.height)]; completion:^(BOOL finished) [swipeLabel removeFromSuperview]; swipeLabel = nil; ]; 太棒了!还要确保禁用“剪辑到边界”,否则标签将不可见。 太棒了。谢谢。【参考方案2】:

对于正在寻找 VaporwareWolf 答案的 Swift 版本的任何人,这里是:

func animateRevealHideActionForRow(tableView: UITableView, indexPath: IndexPath) 
    let cell = tableView.cellForRow(at: indexPath)

    // Should be used in a block
    var swipeLabel: UILabel? = UILabel.init(frame: CGRect(x: cell!.bounds.size.width,
                                                          y: 0,
                                                          width: 200,
                                                          height: cell!.bounds.size.height))

    swipeLabel!.text = "  Swipe Me";
    swipeLabel!.backgroundColor = UIColor.init(red: 255/255, green: 41/255, blue: 53/255, alpha: 1) // Red
    swipeLabel!.textColor = UIColor.white
    cell!.addSubview(swipeLabel!)

    UIView.animate(withDuration: 0.3, animations: 
        cell!.frame = CGRect(x: cell!.frame.origin.x - 100, y: cell!.frame.origin.y, width: cell!.bounds.size.width + 100, height: cell!.bounds.size.height)
    )  (finished) in
        UIView.animate(withDuration: 0.3, animations: 

            cell!.frame = CGRect(x: cell!.frame.origin.x + 100, y: cell!.frame.origin.y, width: cell!.bounds.size.width - 100, height: cell!.bounds.size.height)

        , completion:  (finished) in
            swipeLabel?.removeFromSuperview()
            swipeLabel = nil;
        )
    

【讨论】:

【参考方案3】:

在他们实现该功能之前,不要采用 hacky 方式并使用这样的库。

https://github.com/SwipeCellKit/SwipeCellKit

此库可让您执行以下操作:cell.showSwipe(orientation: .right, animated: true)

【讨论】:

【参考方案4】:

对于 Swift 3 版本,您希望为每一行调用此函数。

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) 

    let cell = tableView.cellForRow(at: indexPath);
    UIView.animate(withDuration: 0.3, animations: 

        cell!.frame = CGRect(x: cell!.frame.origin.x - 100, y: cell!.frame.origin.y, width: cell!.bounds.size.width + 100, height: cell!.bounds.size.height)

    )  (finished) in
        UIView.animate(withDuration: 0.3, animations: 

            cell!.frame = CGRect(x: cell!.frame.origin.x + 100, y: cell!.frame.origin.y, width: cell!.bounds.size.width - 100, height: cell!.bounds.size.height)

        , completion:  (finished) in
        )
    

【讨论】:

【参考方案5】:

VaporwareWolf 答案的另一个修改版本,这个显示两个矩形而不是一个(与 iOS 11 的 UISwipeActionsConfiguration API 配合得很好)。我将它用于具有两个标签、布局有约束的单元格,并且只有在为约束而不是实际的单元格或标签设置动画时才能使其正常工作。

func showActions(forRow row:Int) 
    guard let cell = (tableView.cellForRow(at: IndexPath(row: row, section: 0))) as? RequestTableViewCell else 
        return
    

    let labelWidth:CGFloat = 20

    let createLabel:(UIColor)->UILabel = 
        color in
        let label = UILabel(frame: CGRect.zero)
        label.backgroundColor = color
        label.clipsToBounds = false
        label.frame = CGRect(x: cell.bounds.width, y: 0, width: labelWidth, height: cell.bounds.height)
        return label
    

    let greenLabel = createLabel(.green)
    let redLabel = createLabel(.red)

    //ordering of the subviews is key to get it to look right
    cell.insertSubview(greenLabel, aboveSubview: cell.contentView)
    cell.insertSubview(redLabel, belowSubview: greenLabel)

    let originalLabelSeparationContstraint = cell.labelSeparationContstraint

    UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 3, options: .curveEaseOut, animations: 
        cell.leftLabelLeadingConstraint.constant -= (labelWidth * 2)
        cell.labelSeparationContstraint.constant = cell.requestAgeLabel.frame.minX - cell.nameLabel.frame.maxX
        cell.rightLabelTrailingContstraint.constant += (labelWidth * 2)
        cell.layoutIfNeeded()

        greenLabel.transform = greenLabel.transform.translatedBy(x: -(labelWidth), y: 0)
        redLabel.transform = redLabel.transform.translatedBy(x: -(labelWidth * 2), y: 0)
    , completion: 
        _ in
        UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 3, options: .curveEaseOut, animations: 
            cell.leftLabelLeadingConstraint.constant += (labelWidth * 2)
            cell.labelSeparationContstraint = originalLabelSeparationContstraint
            cell.rightLabelTrailingContstraint.constant -= (labelWidth * 2)
            cell.layoutIfNeeded()

            greenLabel.transform = CGAffineTransform.identity
            redLabel.transform = CGAffineTransform.identity
        , completion: 
            _ in
            greenLabel.removeFromSuperview()
            redLabel.removeFromSuperview()
        )
    )

【讨论】:

【参考方案6】:

Swift 3 等效于 Burak 的答案。你可以在任何你想要的地方以编程方式触发这个调用,我把它放在一个辅助函数中,所以任何表格视图都可以显示这个。用户第一次使用我的应用时,我将其打开(我觉得需要更长的动画时间)。

class func animateRevealHideActionForRow(tableView: UITableView, indexPath: NSIndexPath) 
    let cell = tableView.cellForRow(at: indexPath as IndexPath);

    // Should be used in a block
    var swipeLabel: UILabel? = UILabel.init(frame: CGRect.init(x: cell!.bounds.size.width, y: 0, width: 200, height: cell!.bounds.size.height))

    swipeLabel!.text = "  Swipe Me";
    swipeLabel!.backgroundColor = UIColor.red
    swipeLabel!.textColor = UIColor.white
    cell!.addSubview(swipeLabel!)

    UIView.animate(withDuration: 1.0, animations: 
        cell!.frame = CGRect.init(x: cell!.frame.origin.x - 100, y: cell!.frame.origin.y, width: cell!.bounds.size.width + 100, height: cell!.bounds.size.height)
    )  (finished) in
        UIView.animate(withDuration: 1.0, animations: 
            cell!.frame = CGRect.init(x: cell!.frame.origin.x + 100, y: cell!.frame.origin.y, width: cell!.bounds.size.width - 100, height: cell!.bounds.size.height)
        , completion:  (finished) in
            swipeLabel?.removeFromSuperview()
            swipeLabel = nil;
        )
    

【讨论】:

以上是关于UITableView 以编程方式调用滑动操作的主要内容,如果未能解决你的问题,请参考以下文章

以编程方式打开 UITableView 编辑操作按钮

如何以编程方式向下滑动UITableView以显示底层的UIRefreshControl

以编程方式编辑 UITableView 在所有行上显示删除按钮

以编程方式选择行后未调用 UITableView didSelectRowAtIndexPath

以编程方式在 UIScrollview 水平分页中的 UITableView

iOS 13 Modals - 以编程方式调用滑动解除