缘起于看见书旗小说的列表有点击折叠的动效,觉得十分炫酷。想了三分钟,不知道怎么写。晚上百度了下,知道了大致流程,于是自己实现了下,发现不少坑,于是写下这篇博文
实现原理:
1 tableview cell高度自适应
2 点击cell时,控制cell对应的数据源显示,更新约束后,tableView reloadData
贴下核心代码:
class CellMdl:NSObject { var title:String? var img:UIImage? var detail:String? } class FoldCell:UITableViewCell { var title:UILabel! var detail:UILabel! var imgView:UIImageView! var indicator:UIImageView! var mdl:CellMdl! override init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) addViews() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } func addViews() { } override func layoutSubviews() { title.snp.makeConstraints { (make) in make.top.left.equalTo(self) make.height.equalTo(44) make.right.equalTo(indicator.snp.left) } indicator.snp.makeConstraints { (make) in make.centerY.equalTo(title) make.right.equalTo(self) } imgView.snp.makeConstraints { (make) in make.top.equalTo(title.snp.bottom) make.left.right.equalTo(self) } detail.snp.makeConstraints { (make) in make.top.equalTo(imgView.snp.bottom) make.left.right.bottom.equalTo(self) } } func showIndicatorAni(back:Bool,blk:(()->Void)?) { let rotate = !back ? CGAffineTransform(rotationAngle: .pi) : .identity UIView.animate(withDuration: 0.3, animations: { self.indicator.transform = rotate }) { (finish) in self.detail.text = !back ? self.mdl.detail : nil self.imgView.image = !back ? self.mdl.img : nil // self.layoutIfNeeded() if blk != nil { blk!() } } } // 设置数据源 func setMdl(cellMdl:CellMdl) { mdl = cellMdl title.text = cellMdl.title // detail.text = cellMdl.detail // imgView.image = cellMdl.img } }
这里对应cell声明一个专属cell的model,为cell提供数据
实现autolayout的关键步骤是
1.setModel时,仅将显示的头部视图赋值,未赋值视图应没内容自适应后不会显示,将传入的model用变量存下来(后续使用)
2.点击动画,代码请细看showIndicatorAni函数
这里有两参数,第一个back参数:是否折叠,第二个是个blk,方便外部进行操作。
如果不折叠,即显示全部内容,将model的数据赋值给需要显示的视图;如果折叠,不需要显示全部内容,将不需显示的视图内容清空。
动画完且数据设置完全后,更新约束self layoutIfNeed后将动作传出
然后是外部tableView点击的具体实现
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if previousCell != nil { previousCell?.showIndicatorAni(back: true,blk: nil) } if let foldcell = tableView.cellForRow(at: indexPath) as? FoldCell { weak var weakSelf = self foldcell.showIndicatorAni(back: false,blk: { weakSelf?.tableView.reloadData() }) previousCell = foldcell } }
这里设置个变量previousCell标识前一个选择的cell。为什么写这个变量?选择的cell动画后,前一个cell的动画要还原,并且显示的视图要隐藏,要隐藏,要隐藏!!!
然后新选择的cell.showIndicator,回调中刷新tableview
顺带提一下tableview设置的关键点
1.cell自适应设置姿势
tableView.estimatedHeight = 100
tableView.rowHeight = UITableviewAutomaticDimension
2.cell单选
tableView.allowMutableSelection = false
效果图贴一张