2

I want to animate the subviews of the TableViewCell which is StackView. When I hide the StackView, the TableViewCell height not updating. After googling, I found that I should call tableView.beginUpdates and tableView.endUpdates to notify tableView that there is a change in the cell. The problem is the hide animation and the change of tableview not sync.

Here is the view hierarchy for tableview cell

Content view - Container View (for card shadow) - Container Stack View - [Stack View for label and switch] & [StudentStackView for container of StudentView]

How can I sync the cell height and hide animation the correct way?

Here is the github repo: GitHub

Gif of the App: UIStackView animated not sync with cell height

Jefferson Setiawan
  • 536
  • 1
  • 5
  • 22
  • 2
    For anyone who comes across this question, this problem is caused by the UILabel expanding to fill the visible area. I was able to solve this by adding an empty UIView underneath the label and setting the content hugging priority of the label to required. This causes the blank UIView to grow to fill the space instead of the label. Visually it looks like the cell just collapses – pnavk Jun 15 '20 at 21:52
  • 1
    @pnavk: Content Hugging Priority was key for me - for anyone who comes across this try using the content hugging priority on the label! – Teetz Nov 11 '20 at 10:43

4 Answers4

3

You are right in using beginUpdates()/endUpdates(). Make sure you're not placing the someArrangedSubview.isHidden = true/false in an animate block since the table view and stack view will handle the animations accordingly. When the table view begins update operations, the stack view will resize any arranged subviews that you aren't removing to fill the entire space of the cell (even if you have height constraints on the arranged subview). In my case, the cell content jumped every time I wanted to collapse a cell via removing an arranged subview--so I added a dummy view between the view I wished to remain static* and the collapsible view. The static view won't resize, and the dummy view will expand/collapse as needed. Hope this helps.

*static in the sense that I didn't want the view to move when animating.

nebyark
  • 111
  • 2
  • 5
  • 1
    can you please edit your answer with an elaboration on what the constraints for your dummy view looked like? I'm wrestling with this very same issue, to no avail. Also, how do those constraints on the dummy view not cause the cell's computed height to be unchanged? – BigSauce Oct 13 '18 at 21:14
  • Thank you for dummy view trick. I have a stackview with view: header and detail. When I hide detail view, header was expanding and centering itself, then the stackview was collapsing. I have added a dummy view to bottom of stack view, without any height constraints etc. When the detail view is hidden, dummy view fills its place thus header view does not centers itself inside of the stackview. Then stackview collapse itself and makes dummy view's height 0. – fthdgn Apr 25 '19 at 13:56
1
 `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()
        }
    }`

your studentStackView also know his hide/unhide status while assigning values in function setup.

Rohit Magdum
  • 124
  • 2
0

I left this as a comment but for anyone else experiencing this behavior, the root cause is the UILabel is expanding to fill the visible area before collapsing.

This can be fixed by doing the following 2 things:

  1. Right below the UILabel, insert a Blank UIView
  2. Adjust the Content Hugging Priority of the UILabel to "Required"

With these two adjustments, instead of the UILabel expanding to fill the visible area, the UIView expands instead. Visually, this appears as if the the cell just collapses.

pnavk
  • 4,552
  • 2
  • 19
  • 43
-2

tableView.beginUpdates and tableView.endUpdates are functions that should be called when you are about to modify rowcount or selected state of the rows.

You should try reloadData or reloadrowsatindexpaths, that should take care of the cell height adjustment.

You would better do it using performSelector API so as not to cause recursion in cellForRowAt call stack.

Nirav Bhatt
  • 6,940
  • 5
  • 45
  • 89
  • If I use reloadData, the whole table view get reload and there is no animation. – Jefferson Setiawan Jun 26 '18 at 10:18
  • Inside your toggleInProcess block, you should try handling animations. To get the updated cell height, reload() functions I mentioned above are necessary. You could try putting rowHeight inside heightForRowAtIndexPath function which should return your new layout height - and this gets triggered using reloadData variants. – Nirav Bhatt Jun 26 '18 at 10:45