Swift - 选择 TableView 单元格时动画(didSelectRowAtIndexPath)

Posted

技术标签:

【中文标题】Swift - 选择 TableView 单元格时动画(didSelectRowAtIndexPath)【英文标题】:Swift - Animating When TableView Cell Selected (didSelectRowAtIndexPath) 【发布时间】:2016-03-23 13:46:17 【问题描述】:

当我的 tableviewcell 被选中时,我一直在尝试为它的内容设置动画,但我遇到了问题。我读过一些你不能在 didSelectRowAtIndexPath 中运行 animatewithduration 的地方,或者至少如果你这样做它不会动画。我下面的代码似乎就是这种情况,视图移动但没有动画。

我已尝试将逻辑合并到 willDisplayCell 以监视选择,但无济于事,因此,如果有人能够为我找到正确的解决方案,我会很高兴,因为尽管进行了大量搜索,但似乎没有答案:

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) 
    let currentCellDescriptor = getCellDescriptorForIndexPath(indexPath)
    let cell = tableView.dequeueReusableCellWithIdentifier(currentCellDescriptor["cellIdentifier"] as! String, forIndexPath: indexPath) as! CustomCell
    let indexOfTappedRow = visibleRowsPerSection[indexPath.section][indexPath.row]
    if cellDescriptors[indexPath.section][indexOfTappedRow]["isExpandable"] as! Bool == true 
        if cellDescriptors[indexPath.section][indexOfTappedRow]["isExpanded"] as! Bool == false 


            // INTERESTING BIT: Animates cell contents to Right
            if currentCellDescriptor["cellIdentifier"] as! String == "CellHeader" 
                UIView.animateWithDuration(0.5, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 12, options: .CurveLinear, animations: 
                    cell.headerTitleLeftConstraint.priority = 999
                    cell.headerTitleRightConstraint.priority = 1
                    cell.layoutIfNeeded()
                    , completion: nil)
            

        else if cellDescriptors[indexPath.section][indexOfTappedRow]["isExpanded"] as! Bool == true 
           // MARK: Animates cell contents back to left
           if currentCellDescriptor["cellIdentifier"] as! String == "CellHeader" 
                UIView.animateWithDuration(0.5, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 12, options: .CurveLinear, animations: 
                    cell.headerTitleLeftConstraint.priority = 1
                    cell.headerTitleRightConstraint.priority = 999
                    cell.layoutIfNeeded()
                     , completion: nil)
                    
            

【问题讨论】:

你为什么用'let cell = tableView.dequeueReusableCellWithIdentifier(currentCellDescriptor["cellIdentifier"] as!String, forIndexPath: indexPath) as! 'didSelectRowAtIndexPath' 中的 CustomCell' ? Vader 先生,这是因为我有一个包含所有单元格数据的 plist。这样做要容易得多,因为我有多个可折叠级别的单元格。 【参考方案1】:

首先,不要在这里使用dequeueReusableCellWithIdentifier。它将使用屏幕上不可见的单元格并准备显示它。你想要的是cellForRowAtIndexPath,它返回一个已经在屏幕上给定indexPath的单元格。

然后我知道您想使用 2 个约束并为布局更改设置动画。为此,动画应该只包含layoutIfNeeded:

cell.headerTitleLeftConstraint.priority = 999
cell.headerTitleRightConstraint.priority = 1
UIView.animateWithDuration(0.5, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 12, options: .CurveLinear, animations: 
                cell.layoutIfNeeded()
                , completion: nil)

我还建议您将此逻辑从控制器转移到您的 CustomCell 类。例如,使用selected 属性和setSelected(animated: Bool) 来直观地处理状态变化。

【讨论】:

【参考方案2】:

在 Tanguy 的帮助下,这就是我们目前的情况。我仍然有一些动画问题,因为表格本身实际上不会为下面的下一级单元格设置动画,但我会改进该代码并使其正常工作。认为这现在适合目的,因此值得发布以展示解决方案本身。谢谢!!

 override func setSelected(selected: Bool, animated: Bool) 
    super.setSelected(selected, animated: animated)
        // Adjusts constraints everytime switch is tapped
        isLeftAligned = !isLeftAligned
            if userHanded == "Right" 
            UIView.animateWithDuration(0.5, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 12, options: .CurveLinear, animations: 
                self.leftHandedHeaderConstraint.priority = (self.isLeftAligned) ? 1 : 999
                self.rightHandedHeaderConstraint.priority = (self.isLeftAligned) ? 999 : 1
                self.layoutIfNeeded()
            , completion: nil)

         else if userHanded == "Left" 
            UIView.animateWithDuration(0.5, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 12, options: .CurveLinear, animations: 
                self.leftHandedHeaderConstraint.priority = (self.isLeftAligned) ? 999 : 1
                self.rightHandedHeaderConstraint.priority = (self.isLeftAligned) ? 1 : 999
                self.layoutIfNeeded()
                , completion: nil)
        

【讨论】:

P.S.其他人请注意确保将 tableView 设置为 MultipleSelection 以便并非所有相同类型的单元格都动画:)【参考方案3】:

您不需要使用UIView.animate(...)UITableView 默认提供动画更新。更改约束后,您可以通过调用来实现:

tableView.performBatchUpdates(nil, completion: nil)     

在创建 height 属性时,不要忘记赋予它比.required 更低的优先级。否则 AutoLayout 将在更新时打破约束(您可以在调试器中观察到这一点)。而不是将约束的优先级设置为999 或类似的东西,您应该使用UILayoutPriority 的静态属性以获得更易读的代码。

/// Set this wherever you create your height constraint, e.g. in your custom cell's class.
heightConstraint?.priority = .defaultHigh

didSelectRowAt 的完整代码如下所示:

    let expandedHeight: CGFloat = 500
    let defaultHeight: CGFloat = 85

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) 
      guard let cell = tableView.cellForRow(at: indexPath) as? YourCustomTableViewCell else  return 

      /// Save the status of your cell in a property of your view model.
      if viewModel[indexPath.row].isFullsize 
        cell.heightConstraint?.constant = defaultHeight
        viewModel[indexPath.row].isFullsize = false
       else 
        cell.heightConstraint?.constant = expandedHeight
        viewModel[indexPath.row].isFullsize = true
      

      /// Call this for an animated update (since ios 11)
      tableView.performBatchUpdates(nil, completion: nil)

      /// For the same result you could also use
      /// tableView.beginUpdates()
      /// tableView.endUpdates()
      /// ...but the docs say that this will become deprecated soon

      /// Scrolls to your selected cell's top
      tableView.scrollToRow(at: indexPath, at: .top, animated: true)
    

【讨论】:

以上是关于Swift - 选择 TableView 单元格时动画(didSelectRowAtIndexPath)的主要内容,如果未能解决你的问题,请参考以下文章

重用自定义 tableView 单元格时重复显示相同的子视图(Swift 4.1)

当我在按钮操作上从 TableView 中删除单元格时,应用程序崩溃了 - iOS Swift

TableView - 选择单元格时的操作

`tableView` 在选择单元格时向上移动

选择 TableView 单元格时无法显示另一个 ViewController

为 iOS 选择 TableView 单元格时视图已更改