UILabel in UITableViewCell with auto layout has wrong height

前端 未结 6 944
孤独总比滥情好
孤独总比滥情好 2021-02-01 19:00

I have a UITableView with cells that have a fixed height of 100 points. The cells are created in a xib file that uses 3 constraints to pin a UILabel to

相关标签:
6条回答
  • 2021-02-01 19:26

    Adding this in the cell subclass works:

    - (void)layoutSubviews
    {
        [super layoutSubviews];
        [self.contentView layoutIfNeeded];
        self.myLabel.preferredMaxLayoutWidth = self.myLabel.frame.size.width;
    }
    

    I found this on http://useyourloaf.com/blog/2014/02/14/table-view-cells-with-varying-row-heights.html.

    Update 1: This answer was for iOS 7. I find auto layout in table view cells to be very unreliable since iOS 8, even for very simple layouts. After lots of experimentation, I (mostly) went back to doing manual layout and manual calculation of the cell's height.

    Update 2: I've run some tests on iOS 9 and it seems that UITableViewAutomaticDimension finally works as advertised. Yay!

    0 讨论(0)
  • 2021-02-01 19:27

    Stupid bug! I've lost almost one day in this problem and finally I solved It with Steven Vandewghe's solution.

    Swift version:

    override func layoutSubviews() {
        super.layoutSubviews()
        self.contentView.layoutIfNeeded()
        self.myLabel.preferredMaxLayoutWidth = self.myLabel.frame.size.width
    }
    
    0 讨论(0)
  • 2021-02-01 19:36

    I'm using XCode 10 with iOS 12 and I still get autolayout problems with cells not being given the correct height when the table is first presented. Timothy Moose's answer didn't fix the problem for me, but based on his explanation I came up with a solution which does work for me.

    I subclass UITableViewController and override the viewDidLayoutSubviews message to check for width changes, and then force a table update if the width does change. This fixes the problem before the view is presented, which makes it look much nicer than my other efforts.

    First add a property to your custom UITableViewController subclass to track the previous width:

    @property (nonatomic) CGFloat previousWidth;
    

    Then override viewDidLayoutSubviews to check for width changes:

    - (void)viewDidLayoutSubviews {
        [super viewDidLayoutSubviews];
    
        CGFloat width = self.view.frame.size.width;
        if (self.previousWidth != width) {
            self.previousWidth = width;
            [self.tableView beginUpdates];
            [self.tableView endUpdates];
        }
    }
    

    This fixed issues with my table cells sometimes being given the wrong height initially.

    0 讨论(0)
  • 2021-02-01 19:36

    I usually add these two lines to viewDidLoad()

        self.tableView.rowHeight = UITableViewAutomaticDimension
        self.tableView.estimatedRowHeight = 96
    

    This will automatically resize the cell

    0 讨论(0)
  • 2021-02-01 19:39

    I know this is an old issue, but maybe this UILabel subclass can also help for some:

    class AutoSizeLabel: UILabel {
        override var bounds: CGRect {
            didSet {
                if bounds.size.width != oldValue.size.width {
                    self.setNeedsUpdateConstraints()
                }
            }
        }
    
        override func updateConstraints() {
            if self.preferredMaxLayoutWidth != self.bounds.size.width {
                self.preferredMaxLayoutWidth = self.bounds.size.width
            }
            super.updateConstraints()
        }
    }
    

    Note: works also for cases when your UILabel won't size itself correctly when inside of a StackView

    0 讨论(0)
  • 2021-02-01 19:46

    Since you're constraining the label's width, the intrinsicContentSize honors that width and adjusts the height. And this sets up a chicken and egg problem:

    1. The cell's Auto Layout result depends on the label's intrinsicContentSize
    2. The label's intrinsicContentSize depends on the label's width
    3. The label's width depends on the cell's Auto Layout result

    So what happens is that the cell's layout is only calculated once in which (2) is based on the static width in the XIB file and this results in the wrong label height.

    You can solve this by iterating. That is, repeat the Auto Layout calculation after the label's width has been set by the first calculation. Something like this in your custom cell will work:

    - (void)drawRect:(CGRect)rect
    {
        CGSize size = self.myLabel.bounds.size;
        // tell the label to size itself based on the current width
        [self.myLabel sizeToFit];
        if (!CGSizeEqualToSize(size, self.myLabel.bounds.size)) {
            [self setNeedsUpdateConstraints];
            [self updateConstraintsIfNeeded];
        }
        [super drawRect:rect];
    }
    

    original solution does not work reliably:

    - (void)layoutSubviews
    {
        [super layoutSubviews];
        // check for need to re-evaluate constraints on next run loop
        // cycle after the layout has been finalized
        dispatch_async(dispatch_get_main_queue(), ^{
            CGSize size = self.myLabel.bounds.size;
            // tell the label to size itself based on the current width
            [self.myLabel sizeToFit];
            if (!CGSizeEqualToSize(size, self.myLabel.bounds.size)) {
                [self setNeedsUpdateConstraints];
                [self updateConstraintsIfNeeded];
            }
        });
    }
    
    0 讨论(0)
提交回复
热议问题