嵌套Stack View的Collection View:点击按钮带动画隐藏内层Stack View
实现可折叠Collection View Cell的带动画效果
嘿,我来帮你搞定这个可折叠动画的问题!你的布局结构没问题,现在核心是要把直接设置isHidden的逻辑改成带平滑动画的方式,同时还要让CollectionView感知到Cell高度的变化,实现整体流畅的折叠/展开效果。下面分步骤给你拆解:
第一步:修改Collection View Cell的代码
首先要给按钮添加点击事件,用动画切换内层StackView的显示状态,同时通过闭包通知Controller更新布局:
class YourCollectionViewCell: UICollectionViewCell { // 假设你已经连接了这些IBOutlet @IBOutlet weak var filterTitleButton: UIButton! @IBOutlet weak var expandableStackView: UIStackView! var filter: Filter! // 用来通知Controller状态变化的闭包 var didToggleExpansion: (() -> Void)? override init(frame: CGRect) { super.init(frame: frame) setupButtonTapAction() } required init?(coder: NSCoder) { super.init(coder: coder) setupButtonTapAction() } private func setupButtonTapAction() { filterTitleButton.addTarget(self, action: #selector(toggleExpansion), for: .touchUpInside) } @objc private func toggleExpansion() { // 用0.3秒的动画切换隐藏状态,强制更新布局让动画生效 UIView.animate(withDuration: 0.3) { self.expandableStackView.isHidden = !self.expandableStackView.isHidden self.contentView.layoutIfNeeded() } // 通知Controller需要更新Cell高度 didToggleExpansion?() } func setupUI(isExpanded: Bool, filter: Filter) { self.filter = filter filterTitleButton.setTitle(filter.filterTitle, for: .normal) // 初始化状态时直接设置,不需要动画 expandableStackView.isHidden = !isExpanded } }
第二步:在Controller中管理折叠状态并更新CollectionView
你需要维护一个数组记录每个Cell的展开/折叠状态,当按钮被点击时,切换状态并让CollectionView带动画更新Cell高度:
class YourViewController: UICollectionViewController { var filters: [Filter] = [] // 用数组记录每个Cell的展开状态,默认全部折叠(false) private var expandedStates: [Bool] = [] override func viewDidLoad() { super.viewDidLoad() // 初始化状态数组(假设filters已经提前赋值) expandedStates = Array(repeating: false, count: filters.count) // 配置CollectionView的FlowLayout,开启自动高度计算 if let flowLayout = collectionViewLayout as? UICollectionViewFlowLayout { flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize flowLayout.minimumLineSpacing = 8 // 可选:设置Cell之间的间距 } } override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return filters.count } override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "YourCellIdentifier", for: indexPath) as! YourCollectionViewCell let isExpanded = expandedStates[indexPath.item] cell.setupUI(isExpanded: isExpanded, filter: filters[indexPath.item]) // 设置闭包,处理按钮点击后的状态更新 cell.didToggleExpansion = { [weak self] in guard let self = self else { return } // 切换当前Cell的展开状态 self.expandedStates[indexPath.item] = !self.expandedStates[indexPath.item] // 带动画更新CollectionView布局,让Cell高度平滑变化 self.collectionView.performBatchUpdates(nil, completion: nil) } return cell } }
第三步:确保Auto Layout约束正确
这是关键!你的Cell必须有完整的约束链,让CollectionView能自动计算高度:
- 外层StackView的top、leading、trailing、bottom都要和ContentView的对应边缘对齐,没有歧义。
- 内层StackView的约束也要完整,确保它显示/隐藏时,外层StackView的高度能自动调整。
注意事项
- 不要在
setupUI里用UIView.performWithoutAnimation包裹状态设置,除非是初始化场景;动画要放在按钮点击的方法里。 - 使用
UICollectionViewFlowLayout.automaticSize是让Cell高度自动适配的核心,不需要手动计算高度。 performBatchUpdates会让CollectionView的布局更新带有动画,和Cell内部的StackView动画同步,整体效果更流畅。
内容的提问来源于stack exchange,提问作者Mohanraj
相关产品推荐
相关产品推荐

