UITableView 自定义插入动作按钮 onSwipe

Posted

技术标签:

【中文标题】UITableView 自定义插入动作按钮 onSwipe【英文标题】:UITableView Custom Inset Action Button onSwipe 【发布时间】:2018-09-21 07:29:38 【问题描述】:

我能够制作一个 UITableViewCell 可滑动显示 UIButton 改编 Ray Wenderlich 的演练 here。

目标是一个全方位插图contentView,其中onPanLeft 会从右侧拉入具有类似顶部/底部插图的uiButton

我已经做到了,但是我很难解决一个问题:

    如果我开始缓慢地向左拖动,contentView 会迅速左右来回移动。

初始 contentView 状态,UITableViewCell 容器的前导/尾随填充为 20 pt。

以扩展宽度结束 contentView 状态(黄线是蓝色/灰色子视图应该遇到的位置,蓝色子视图宽度增加 - 已修复)

以下是我的相关代码供参考:

var panStartPoint = CGPoint.zero
var startingRightLayoutConstraintConstant: CGFloat = 20

let kBounceValue: CGFloat = 20.0

func buttonTotalWidth() -> CGFloat 
    return deleteButton.frame.width


func updateConstraintsIfNeeded(_ animated: Bool, completion: @escaping (_ finished: Bool) -> Void) 
    var duration: Float = 0
    if animated 
        duration = 0.1
    

    UIView.animate(withDuration: TimeInterval(duration), delay: 0, options: .curveEaseOut, animations: 
        self.layoutIfNeeded()
    , completion: completion)


@objc func panThisCell(_ sender: UIPanGestureRecognizer) 

    if let cell = sender.view?.superview?.superview as? PostTableViewCell 
        switch sender.state 
        case .began:

            cell.panStartPoint = sender.translation(in: cell.myContentView)
            cell.startingRightLayoutConstraintConstant = cell.contentViewRightConstraint.constant
            break
        case .changed:
            let currentPoint: CGPoint = sender.translation(in: cell.myContentView)
            let deltaX: CGFloat = currentPoint.x - cell.panStartPoint.x
            var panningLeft = false

            if (currentPoint.x < cell.panStartPoint.x)  panningLeft = true 

            if (cell.startingRightLayoutConstraintConstant == 20) 
                if (!panningLeft) 
                    let constant: CGFloat = max(-deltaX, 20)

                    if (constant == 20) 
                        resetConstraintContstants(cell, toZero: true, notifyDelegateDidClose: true)
                     else 
                        cell.contentViewLeftConstraint.constant = 20 - constant
                        cell.contentViewRightConstraint.constant = 20 + constant
                    
                 else 
                    let constant: CGFloat = min(-deltaX, cell.buttonTotalWidth())
                    if (constant == cell.buttonTotalWidth()) 
                        setConstraintsToShowAllButtons(cell, true, notifyDelegateDidOpen: true)
                     else 
                        cell.contentViewLeftConstraint.constant = 20 - constant
                        cell.contentViewRightConstraint.constant = 20 + constant
                    
                
             else 
                let adjustment: CGFloat = cell.startingRightLayoutConstraintConstant - deltaX
                if (!panningLeft) 
                    let constant: CGFloat = max(adjustment, 20)
                    if (constant == 20) 
                        resetConstraintContstants(cell, toZero: true, notifyDelegateDidClose: true)
                     else 
                        cell.contentViewLeftConstraint.constant = 20 - constant
                        cell.contentViewRightConstraint.constant = 20 + constant
                    
                 else 
                    let constant: CGFloat = min(adjustment, cell.buttonTotalWidth())
                    if (constant == cell.buttonTotalWidth()) 
                        setConstraintsToShowAllButtons(cell, true, notifyDelegateDidOpen: true)
                     else 
                        cell.contentViewLeftConstraint.constant = 20 - constant
                        cell.contentViewRightConstraint.constant = 20 + constant
                    
                
            
        break
        case .ended:
            let halfOfButton: CGFloat = cell.deleteButton.frame.width / 2
                if cell.contentViewRightConstraint.constant >= halfOfButton 


                    setConstraintsToShowAllButtons(cell, true, notifyDelegateDidOpen: true)
                 else 
                    resetConstraintContstants(cell, toZero: true, notifyDelegateDidClose: true)
                
            break
        case .cancelled:                
            if cell.startingRightLayoutConstraintConstant == 20 
                resetConstraintContstants(cell, toZero: true, notifyDelegateDidClose: true)
             else 
                setConstraintsToShowAllButtons(cell, true, notifyDelegateDidOpen: true)
            
            break
        default:
            break
        
    


func setConstraintsToShowAllButtons(_ cell: PostTableViewCell, _ animated: Bool, notifyDelegateDidOpen notifyDelegate: Bool) 
    if cell.startingRightLayoutConstraintConstant == 20 + cell.buttonTotalWidth() && cell.contentViewRightConstraint.constant == 20 + cell.buttonTotalWidth() 
        return
    
    cell.contentViewLeftConstraint.constant = 20 - cell.buttonTotalWidth() - cell.kBounceValue
    cell.contentViewRightConstraint.constant = 20 + cell.buttonTotalWidth() + cell.kBounceValue
    cell.deleteButtonTrailingConstraint.constant = cell.kBounceValue

    cell.updateConstraintsIfNeeded(animated)  finished in

        cell.contentViewLeftConstraint.constant = 20 - cell.buttonTotalWidth()
        cell.contentViewRightConstraint.constant = 20 + cell.buttonTotalWidth()
        cell.deleteButtonTrailingConstraint.constant = 0

        cell.updateConstraintsIfNeeded(animated)  finished in

            cell.startingRightLayoutConstraintConstant = cell.contentViewRightConstraint.constant
        
    


func resetConstraintContstants(_ cell: PostTableViewCell,toZero animated: Bool, notifyDelegateDidClose notifyDelegate: Bool) 
    if cell.startingRightLayoutConstraintConstant == 20 && cell.contentViewRightConstraint.constant == 20 
        //Already all the way closed, no bounce necessary
        return
    

    cell.contentViewRightConstraint.constant = 20 - cell.kBounceValue
    cell.contentViewLeftConstraint.constant = 20 + cell.kBounceValue
    cell.deleteButtonTrailingConstraint.constant = -cell.deleteButton.frame.width - cell.kBounceValue

    cell.updateConstraintsIfNeeded(animated)  finished in
        cell.contentViewRightConstraint.constant = 20
        cell.contentViewLeftConstraint.constant = 20
        cell.deleteButtonTrailingConstraint.constant = -cell.deleteButton.frame.width

        cell.updateConstraintsIfNeeded(animated)  finished in
            cell.startingRightLayoutConstraintConstant = cell.contentViewRightConstraint.constant
        
    

【问题讨论】:

【参考方案1】:

您可以尝试使用您的图片,如下所示。

override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [AnyObject]? 

    let moreClosure =  (action: UITableViewRowAction!, indexPath: NSIndexPath!) -> Void in
        println("More closure called")
    

    let moreAction = UITableViewRowAction(style: .Normal, title: "  ", handler: moreClosure)

    if let image = UIImage(named: "image.png")
        moreAction.backgroundColor = UIColor(patternImage: image)

    
    return [moreAction]

它在我的应用程序中运行良好。

【讨论】:

我知道这个实现是可用的,但它是否允许“moreAction”按钮具有 uitableview 的顶部/底部插入和角半径?【参考方案2】:

我在几年前写了问题中引用的原始教程,ios 7 出来后不久。

老实说,我真的建议现在使用 tableView(_:editActionsForRowAt:)tableView(_:trailingSwipeActionsConfigurationForRowAt:) UITableViewDelegate 方法。它们得到了官方支持,并且方式比我在那里尝试破解的更简单。

第一个是 iOS 8 及更高版本,第二个是 iOS 11 及更高版本。我还怀疑,自从编写该教程以来,他们可能已经更改了单元的一些底层架构..

【讨论】:

以上是关于UITableView 自定义插入动作按钮 onSwipe的主要内容,如果未能解决你的问题,请参考以下文章

如何使用文本字段 UITableView 从自定义单元格中获取值?

将 UISearchBar 放在自定义 UITableview 中会导致奇怪的动画

使用自定义单元格反转 UITableView

为啥 UITableView 自定义单元格的第一个单元格总是在 Xcode 中的动作事件上被调用两次

WordPress 自定义插入到帖子按钮

UITableview中的按钮修改当前单元格自定义