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

Swift - Animating When TableView Cell Selected (didSelectRowAtIndexPath)

我一直在尝试在我的 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)
                    }
            }

首先,不要在这里使用dequeueReusableCellWithIdentifier。它将使用屏幕上不可见的单元格并准备显示它。您想要的是 cellForRowAtIndexPath 给定 indexPath.

屏幕上已经存在的 returns 单元格

然后我明白你想玩 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 class。例如,使用 selected 属性和 setSelected(animated: Bool) 来直观地处理状态变化。

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

 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)
        }
}

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

tableView.performBatchUpdates(nil, completion: nil)     

在创建身高时,请不要忘记将身高 属性 设置为低于 .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)
    }