简体   繁体   中英

Animation is weird when hiding subView in UIStackView in Cell

Here is the situation. A title label, a "read more" button and a content label in a stackView. And the stackView is in a cell, set Auto-Layout. The height of tableView's cell is set to AutoDimension. When I tap button, content label will show or hide.

The button's action method is

@IBAction func readMore(_ sender: Any) {
  tableView.performBatchUpdates({
    self.contentLabel.isHidden.toggle()
  }, completion: nil)
}

Here is the result in slow animations:

在此处输入图片说明

As you can see, when the content is going to show, line 2 is presented firstly, ie content is presented from the center. When the content is going to hide, the content label is hidden instantly, and button is stretched to the frame that content label had before hiding. This animation is strange.

Furthermore, if I set the stackView's spacing to 10, the case becomes worse. The title label was also affected:

在此处输入图片说明

I adjusted everything I can,stackView's distribution, three subViews' content mode and content hugging/compression priority. I can't find an appropriate way to fix it.

Here is the ideal result:

在此处输入图片说明

I achieved it by a little tricky way:

@IBAction func readMore(_ sender: Any) {
  tableView.performBatchUpdates({
    UIView.animate(withDuration: 0.3) { // It must be 0.3
      self.contentLabel.isHidden.toggle()
    }
  }, completion: nil)
}

I'm not sure this is the most appropriate way to fix it. So I want to know why this weird animation happens and if there is a more appropriate way to fix it. Thanks!

I have had to do a similar animation several times before. The way to solve this is to define the height of your stack view and independently your cell's content view. Then when you want the cell's height to change, you only update the content view's height constraint.

A good way to determine a views height is to use the intrinsicContentSize property. Override this if you need a different value from the inherited one.

Another way to be notified of a views size change is to create a subclass with a delegate or a closure which is called from the subclassed views frame property and passes the new size to whoever is listening for it.

Animating to hide/reveal multi-line labels can be problematic, particularly when used in a stack view.

If you give it a try, you'll find that even outside of a table view cell - just the stack view in a view - you will see the same issue when toggling the .isHidden property of the label. This is due to the fact that UILabel vertically centers its text.

Here is another approach, which doesn't use a stack view (background colors for clarity):

在此处输入图片说明

Top Label is set to 1 line; Read More is a normal button, Bottom Label is set to 0 lines.

You will notice the pink rectangle. That is a UIView which I've named ShimView - more about that shortly.

The Top Label is constrained Top: 4, Leading: 8, Trailing: 8 The Button is constrained Top: 0 (to topLabel), Leading: 8, Trailing: 8 The Bottom Label is constrained Top: 0 (to button), Leading: 8, Trailing: 8

The "shim view" is constrained Trailing: 8, Top: 0 (*to the top of bottom label*), Bottom: 4 (to the contentView)

The "shim view" is also given a Height constraint of 21 , with Priority: 999 -- and that Height constraint is connected to an IBOutlet in the cell class.

The key is that we will adjust the shim's Height constraint's .constant to expand/collapse the cell.

On init, we set the .constant to 0 - this will leave the Bottom Label at its content-determined height, but won't be visible because it will be clipped by the cell's contentView.

When we want to "reveal/conceal" the label, we'll animate the height .constant of the shim.

Result:

在此处输入图片说明

And, the result after clearing the background colors:

在此处输入图片说明

Here is the code:

//
//  ExpandCollapseTableViewController.swift
//
//  Created by Don Mag on 6/19/18.
//

import UIKit

class ExpandCollapseCell: UITableViewCell {

    @IBOutlet var topLabel: UILabel!
    @IBOutlet var theButton: UIButton!
    @IBOutlet var bottomLabel: UILabel!
    @IBOutlet var theShim: UIView!
    @IBOutlet var shimHeightConstraint: NSLayoutConstraint!

    var myCallBack: (() -> ())?

    @IBAction func didTap(_ sender: Any) {
        myCallBack?()
    }

}

class ExpandCollapseTableViewController: UITableViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.rowHeight = UITableViewAutomaticDimension
        tableView.estimatedRowHeight = 100
    }

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 4
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "ExpandCollapseCell", for: indexPath) as! ExpandCollapseCell

        cell.topLabel.text = "Index Path - \(indexPath)"
        cell.bottomLabel.text = "Line 1\nLine 2\nLine 3\nLine 4"

        // init to "collapsed"
        // in actual use, this would be tracked so the row would remain expanded or collapsed
        // on reuse (when the table is scrolled)
        cell.shimHeightConstraint.constant = 0

        if true {
            cell.topLabel.backgroundColor = .clear
            cell.theButton.backgroundColor = .clear
            cell.bottomLabel.backgroundColor = .clear
            cell.theShim.backgroundColor = .clear
        }

        cell.myCallBack = {
            UIView.animate(withDuration: 0.3) { // It must be 0.3
                self.tableView.beginUpdates()
                cell.shimHeightConstraint.constant = (cell.shimHeightConstraint.constant == 0) ? cell.bottomLabel.frame.size.height : 0
                self.tableView.layoutIfNeeded()
                self.tableView.endUpdates()
            }
        }

        return cell
    }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM