首页 > 解决方案 > 动画 tableView 的高度以适应屏幕上的内容

问题描述

我正在构建一个嵌入在容器中的 tableView 叠加层。主要目标是让 tableView 对其高度进行动画处理以适应屏幕上的内容。经过长时间的试验和错误后,我想出了以下方法,但非常老套:

1)当dataSource改变时重新加载tableView。

2)更新tableView的高度约束viewDidLayoutSubviews()

3) 调用layoutIfNeeded()动画闭包。

4) 在回调中设置容器的高度。

class VC: UIViewController {
    let tableView = UITableView()
        // This callback is used to updater overlayContainerView height to match tableView's contentSize height.
        public var layoutCallback: (_ value: CGFloat)->() = { _ in }

    var dataSource: ModalCardOverlayDataSource? = nil { didSet {
        tableView.dataSource = dataSource
        self.tableView.reloadData()
        view.setNeedsLayout()
        }

        .
        .
        .
        .

        override public func viewDidLayoutSubviews() {
            super.viewDidLayoutSubviews()
            self.tableView.snp.updateConstraints { make in
                make.height.equalTo(self.tableView.contentSize.height)
            }

            UIView.animate(withDuration: 0.3, animations: {
                self.view.layoutIfNeeded()
            })
            layoutCallback(tableView.contentSize.height)
        }
    }
}

该解决方案效果很好,但有一个缺点:只要高度保持不变,视图就会向下动画一点,然后在另一次刷新时返回到正确的高度。

当我删除动画代码时,问题不会发生。我想这是 UIKit 的一些固有问题。

潜在的简单解决方法是引入额外的变量来跟踪 tableview 的高度,当它保持不变时,动画代码被跳过。但我想了解是什么导致了这个问题。

override public func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        print("the height is \(self.tableView.contentSize.height)")
        self.tableView.snp.updateConstraints { make in
            make.height.equalTo(self.tableView.contentSize.height)
        }

        UIView.animate(withDuration: 0.3, animations: {
            print("the height in animation is \(self.tableView.contentSize.height)")
            self.view.layoutIfNeeded()
        })
        print("the height in CALLBACK is \(self.tableView.contentSize.height)")
        layoutCallback(tableView.contentSize.height)
    }

当我在刷新具有相同内容的数据源时打印内容视图高度时,这就是我得到的

初始值

the height is 132.0 the height in animation is 132.0 the height is 132.0 the height in animation is 132.0 the height in CALLBACK is 275.33333587646484 the height in CALLBACK is 275.33333587646484 the height is 275.33333587646484 the height in animation is 275.33333587646484 the height is 275.3333333333333 the height in animation是 275.3333333333333 高度是 275.3333333333333 动画中的高度是 275.3333333333333 回调中的高度是 275.3333333333333 回调中的高度是 275.3333333333333 回调中的高度是 3333333333333333333

第一次刷新:

高度为 132.0 动画高度为 132.0 高度为 132.0 动画高度为 132.0 CALLBACK 高度为 275.33333587646484 CALLBACK 高度为 275.33333587646484

第二次刷新

the height is 132.0 the height in animation is 132.0 the height in CALLBACK is 132.0 the height is 275.33333587646484 the height in animation is 275.33333587646484 the height is 275.3333333333333 the height in animation is 275.3333333333333 the height is 275.3333333333333 the height in animation is 275.3333333333333 the height in CALLBACK是 275.3333333333333 CALLBACK 中的高度是 275.3333333333333 CALLBACK 中的高度是 275.3333333333333 高度是 275.3333333333333 动画中的高度是 275.3333333333333 CALLBACK 中的高度是 33333333333333333333

有什么想法可能导致这种奇怪的行为吗?为什么动画代码使它在两种高度状态之间切换取决于它是偶数还是奇数数据源重新加载?

标签: iosswiftuitableview

解决方案


您需要创建 tableview 的子类并在 StoryBoard 中使用自动布局。内部子类:-

import UIKit
class SelfSizingTableView: UITableView {

  override var intrinsicContentSize: CGSize {
      self.layoutIfNeeded()
      return self.contentSize
  }

  override var contentSize: CGSize {
     didSet{
        self.invalidateIntrinsicContentSize()
     }
  }
}

并将 tableview rowHeight 设置为 UITableViewAutomaticDimension。


推荐阅读