如何正确地为隐藏在 UITableViewCell 中的 UIStackView 设置动画?

Posted

技术标签:

【中文标题】如何正确地为隐藏在 UITableViewCell 中的 UIStackView 设置动画?【英文标题】:How to animate UIStackView hiding inside UITableViewCell properly? 【发布时间】:2018-06-26 09:11:54 【问题描述】:

我想为 StackView 的 TableViewCell 的子视图设置动画。当我隐藏 StackView 时,TableViewCell 高度没有更新。 google了一下,发现应该调用tableView.beginUpdatestableView.endUpdates通知tableView单元格有变化。问题是隐藏动画和tableview的变化不同步。

这是表格视图单元格的视图层次结构

内容视图 - 容器视图(用于卡片阴影) - 容器堆栈视图 - [用于标签和开关的堆栈视图] & [用于 StudentView 容器的StudentStackView]

如何以正确的方式同步单元格高度和隐藏动画?

这里是 github 仓库:GitHub

应用程序的 Gif:

【问题讨论】:

对于遇到这个问题的任何人来说,这个问题是由 UILabel 扩展以填充可见区域引起的。我可以通过在标签下方添加一个空的 UIView 并将标签的内容拥抱优先级设置为必需来解决这个问题。这会导致空白 UIView 增长以填充空间而不是标签。从视觉上看,它看起来像单元格刚刚折叠 @pnavk:内容拥抱优先级对我来说很关键——任何遇到这种情况的人都可以尝试使用标签上的内容拥抱优先级! 【参考方案1】:

您使用beginUpdates()/endUpdates() 是对的。确保您没有将someArrangedSubview.isHidden = true/false 放置在动画块中,因为表格视图和堆栈视图将相应地处理动画。当表格视图开始更新操作时,堆栈视图将调整您未删除的任何排列子视图的大小以填充单元格的整个空间(即使您对排列的子视图有高度限制)。就我而言,每次我想通过删除排列的子视图来折叠单元格时,单元格内容都会跳跃——所以我在希望保持静态*的视图和可折叠视图之间添加了一个虚拟视图。静态视图不会调整大小,虚拟视图将根据需要展开/折叠。希望这会有所帮助。

*静态的意思是我不希望视图在动画时移动。

【讨论】:

您能否编辑您的答案,详细说明您的虚拟视图的约束是什么样的?我正在努力解决同样的问题,但无济于事。此外,虚拟视图上的这些约束如何不会导致单元格的计算高度不变? 感谢您的虚拟视图技巧。我有一个带有视图的stackview:标题和详细信息。当我隐藏详细视图时,标题正在扩展并自行居中,然后堆栈视图正在折叠。我在堆栈视图的底部添加了一个虚拟视图,没有任何高度限制等。当详细视图被隐藏时,虚拟视图填充它的位置,因此标题视图不会在堆栈视图内居中。然后 stackview 自行折叠并使虚拟视图的高度为 0。【参考方案2】:
 `public func setup(classRoom: ClassRoom, toggleInProcess: @escaping () -> (), toggled: @escaping () -> ()) 
        containerStackView.addArrangedSubview(studentStackView)
        self.nameLabel.text = classRoom.name
        self.activeSwitch.isOn = classRoom.isActive
        self.studentStackView.isHidden = !self.activeSwitch.isOn // Let him know his hide/unhide. 
        for student in classRoom.students 
            let studentView = StudentView()
            studentView.nameLabel.text = student.name
            studentStackView.addArrangedSubview(studentView)
        
        activeSwitch.addTarget(self, action: #selector(toggleShowStudents(show:)), for: .valueChanged)
        self.toggleInProcess = toggleInProcess
        self.toggled = toggled
        setupShadow()
    `


`  @objc func toggleShowStudents(show: Bool) 
        UIView.animate(withDuration: 0.3, animations: 
            self.studentStackView.isHidden = !self.activeSwitch.isOn
            self.toggleInProcess()
            self.containerView.layoutIfNeeded()
        )  _ in
            self.toggled()
        
    `

您的 studentStackView 在函数 setup 中分配值时也知道他的隐藏/取消隐藏状态。

【讨论】:

哦,我忘记设置了,但我在实际项目中已经这样做了,而且该代码也不起作用。【参考方案3】:

我将此作为评论留下,但对于遇到此行为的其他人来说,根本原因是 UILabel 在折叠之前正在扩展以填充可见区域。

这可以通过以下两件事来解决:

    在 UILabel 正下方,插入一个空白 UIView 将 UILabel 的 Content Hugging 优先级调整为“必需”

通过这两个调整,UIView 不是扩展来填充可见区域,而是扩展。从视觉上看,这看起来好像单元格刚刚折叠。

【讨论】:

【参考方案4】:

tableView.beginUpdatestableView.endUpdates 是当您要修改行数或行的选定状态时应调用的函数。

您应该尝试 reloadData 或 reloadrowsatindexpaths,这应该可以调整单元格高度。

您最好使用 performSelector API 来执行此操作,以免在cellForRowAt 调用堆栈中引起递归。

【讨论】:

如果我使用 reloadData,整个表格视图会重新加载并且没有动画。 在您的 toggleInProcess 块中,您应该尝试处理动画。要获得更新的单元格高度,我上面提到的 reload() 函数是必要的。您可以尝试将 rowHeight 放在 heightForRowAtIndexPath 函数中,该函数应该返回您的新布局高度 - 这会使用 reloadData 变体触发。

以上是关于如何正确地为隐藏在 UITableViewCell 中的 UIStackView 设置动画?的主要内容,如果未能解决你的问题,请参考以下文章

UITableViewCell,一些不固定显示的控件,动态隐藏有时会显示不正确的解决办法。

如何在 UITableViewCell 单元格中隐藏具有大小的按钮?

如何隐藏 UITableViewCell?请看截图

UITableViewCell 使用自定义 layoutSubviews() 出现在屏幕上后隐藏部分内容

我如何始终保持我的 html 元素粘在父元素的底部,我正在使用溢出:隐藏并隐式地为父元素提供高度?

如何在编辑模式下隐藏 UITableViewCell 右侧的三行“移动”符号?