为 UITableViewCell 高度变化添加动画
source link: https://hanleylee.com/articles/add-animation-for-tableviewcell-when-height-changed/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
为 UITableViewCell 高度变化添加动画
如何让 UITableView 的 cell 高度动态变化且有动画效果呢?
如题, 要想达到目的, 我目前总结有两种方式:
- 使用 UITableView 的
func reloadRows(at indexPaths: [IndexPath], with animation: UITableView.RowAnimation)
方法- 优点: 调用简单, 只需要针对指定 cell 做出高度变更, 然后将该 cell 的 indexPath 传入此方法即可
- 缺点:
- 会调用针对该 cell 调用
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
等代理方法 - 刷新时界面会有闪烁现象
- 会调用针对该 cell 调用
- 使用 UITableView 的
func beginUpdates()
与func endUpdates()
方法- 优点: 可以自定义动画时长与效果, 更加灵活, 在对指定 cell 做出改变后, 调用此方法会触发所有 cell 的高度计算并刷新, 且高度变化时有动画效果
在 iOS 11 之后, 我们可以用
func performBatchUpdates(_ updates: (() -> Void)?, completion: ((Bool) -> Void)? = nil)
方法来代替beginUpdates
和endUpdates
方法
综上, 我在实际项目中选择方法2作为动态高度动画方案, 具体效果如下:
使用 beginUpdates
与 endUpdates
(或 performBatchUpdates
) 的实际案例代码如下:
- TestTableViewCellExpandableVC.swift
import UIKit class TestTableViewCellExpandableVC: UIViewController { private var tableView: UITableView! override func viewDidLoad() { super.viewDidLoad() initView() } } // MARK: - UITableView Data Source extension TestTableViewCellExpandableVC: UITableViewDataSource { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = (tableView.dequeueReusableCell(withIdentifier: "Cell") as? TestTableViewCellExpandableCell) ?? .init(style: .default, reuseIdentifier: "Cell") cell.setContent(with: "Row \(indexPath.row)") { [weak cell, weak tableView] in if #available(iOS 11.0, *) { tableView?.performBatchUpdates { cell?.updateHeight() } } else { cell?.updateHeight() tableView?.beginUpdates() tableView?.endUpdates() } } return cell } func numberOfSections(in tableView: UITableView) -> Int { return 1 } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 3 } } // MARK: UI extension TestTableViewCellExpandableVC { private func initView() { tableView = .init() tableView.dataSource = self tableView.separatorStyle = .none view.addSubview(tableView) tableView.snp.makeConstraints { make in make.top.equalToSuperview() make.left.right.bottom.equalToSuperview() } } }
- TestTableViewCellExpandableCell.swift
import UIKit class TestTableViewCellExpandableCell: UITableViewCell { enum HeightType { case small case medium case big var height: CGFloat { switch self { case .small: return 100 case .medium: return 200 case .big: return 300 } } var next: HeightType { switch self { case .small: return .medium case .medium: return .big case .big: return .small } } } // MARK: Subviews private var titleLb: UIButton! // MARK: Data private var heightType: HeightType = .small private var tapClosure: () -> Void = {} // MARK: Life Cycle override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) initUI() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } // MARK: - Custom Method extension TestTableViewCellExpandableCell { func setContent(with title: String?, tapClosure: @escaping () -> Void) { titleLb.setTitle(title, for: .normal) self.tapClosure = tapClosure } func updateHeight() { heightType = heightType.next titleLb.snp.updateConstraints { make in // 这里必须使用 .low 以上的优先级, 否则会约束报错 make.height.equalTo(heightType.height).priority(.high) } } @objc private func tap() { tapClosure() } } // MARK: - UI extension TestTableViewCellExpandableCell { private func initUI() { backgroundColor = UIColor.gray titleLb = .init() titleLb.addTarget(self, action: #selector(tap), for: .touchUpInside) titleLb.setTitleColor(.white, for: .normal) titleLb.backgroundColor = UIColor.red contentView.addSubview(titleLb) titleLb.snp.makeConstraints { make in make.height.equalTo(heightType.height).priority(.high) make.width.equalTo(200) make.centerX.equalToSuperview() make.top.bottom.equalToSuperview().inset(5) } let bottomLine = UIView() bottomLine.backgroundColor = .black contentView.addSubview(bottomLine) bottomLine.snp.makeConstraints { make in make.left.right.equalToSuperview().inset(5) make.bottom.equalToSuperview() make.height.equalTo(0.5) } } }
Reference
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK