30

我有一个只包含 UILabel 的视图。此标签包含多行文本。父级具有可变宽度,可以使用平移手势调整大小。我的问题是,当我调整 UILabel 的大小时,它不会重新计算其高度以使所有内容仍然可见,它只是将其切断。

我已经设法通过一些 hack 来修复它,但是运行起来非常慢:

- (void)layoutSubviews {

    CGSize labelSize = [self.labelDescription sizeThatFits:CGSizeMake(self.frame.size.width, FLT_MAX)];

    if (self.constraintHeight) {
        [self removeConstraint:self.constraintHeight];
    }

    self.constraintHeight = [NSLayoutConstraint constraintWithItem:self.labelDescription attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:labelSize.height];

    [self addConstraint:self.constraintHeight];

    [super layoutSubviews];
}

这不应该通过自动布局自动发生吗?

编辑

我的观点结构是:

UIScrollView
---> UIView
     ---> UILabel

以下是 UIScrollView 的约束:

<NSLayoutConstraint:0x120c4860 H:|-(>=32)-[DescriptionView:0x85c81c0]   (Names: '|':UIScrollView:0x85db650 )>,
<NSLayoutConstraint:0x120c48a0 H:|-(32@900)-[DescriptionView:0x85c81c0] priority:900   (Names: '|':UIScrollView:0x85db650 )>,
<NSLayoutConstraint:0x120c48e0 H:[DescriptionView:0x85c81c0(<=576)]>,
<NSLayoutConstraint:0x120c4920 H:[DescriptionView:0x85c81c0]-(>=32)-|   (Names: '|':UIScrollView:0x85db650 )>,
<NSLayoutConstraint:0x120c4960 H:[DescriptionView:0x85c81c0]-(32@900)-| priority:900   (Names: '|':UIScrollView:0x85db650 )>,
<NSLayoutConstraint:0x8301450 DescriptionView:0x85c81c0.centerX == UIScrollView:0x85db650.centerX>,

以下是对 UIView 的约束:

<NSLayoutConstraint:0x85c4580 V:|-(0)-[UILabel:0x85bc7b0]   (Names: '|':DescriptionView:0x85c81c0 )>,
<NSLayoutConstraint:0x85c45c0 H:|-(0)-[UILabel:0x85bc7b0]   (Names: '|':DescriptionView:0x85c81c0 )>,
<NSLayoutConstraint:0x85c9f80 UILabel:0x85bc7b0.trailing == DescriptionView:0x85c81c0.trailing>,
<NSLayoutConstraint:0x85c9fc0 UILabel:0x85bc7b0.centerY == DescriptionView:0x85c81c0.centerY>

UILabel 本身对它没有任何限制,除了将其固定到其父级的边缘

4

3 回答 3

40

好吧,我终于搞定了。解决方案是设置preferredMaxLayoutWidthin viewDidLayoutSubviews,但仅第一轮布局之后。您可以简单地通过将异步调度回主线程来安排这一点。所以:

- (void)viewDidLayoutSubviews {
    dispatch_async(dispatch_get_main_queue(), ^{
        self.theLabel.preferredMaxLayoutWidth = self.theLabel.bounds.size.width;
    });
}

这样,在标签的宽度被其与超级视图相关的约束正确设置之后preferredMaxLayoutWidth,您才进行设置。

这里的工作示例项目:

https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/ch23p673selfSizingLabel4/p565p579selfSizingLabel/ViewController.m

编辑:另一种方法!子类 UILabel 和覆盖layoutSubviews

- (void) layoutSubviews {
    [super layoutSubviews];
    self.preferredMaxLayoutWidth = self.bounds.size.width;
}

结果是一个自调整大小的标签 - 无论其宽度如何变化(假设其宽度由约束/布局更改),它都会自动更改其高度以适应其内容。

于 2012-11-28T23:08:28.763 回答
26

在向 Apple 提出错误后,我已经解决了这个问题。多行文本需要两次布局的问题,这一切都依赖于属性preferredMaxLayoutWidth

以下是需要添加到包含多行标签的视图控制器的相关代码:

- (void)viewWillLayoutSubviews
{
    // Clear the preferred max layout width in case the text of the label is a single line taking less width than what would be taken from the constraints of the left and right edges to the label's superview
    [self.label setPreferredMaxLayoutWidth:0.];
}

- (void)viewDidLayoutSubviews
{
    // Now that you know what the constraints gave you for the label's width, use that for the preferredMaxLayoutWidth—so you get the correct height for the layout
    [self.label setPreferredMaxLayoutWidth:[self.label bounds].size.width];

    // And then layout again with the label's correct height.
    [self.view layoutSubviews];
}
于 2012-11-19T04:09:43.800 回答
3

在适用于 iOS 7/8 的 Xcode 6.1 中,我只需设置preferredMaxLayoutWidth一个在我的视图上调用的 setter 方法来显示标签的文本,就可以让它工作。我猜它是0从一开始就设置好的。下面的示例,self.teachPieceLabel标签在哪里。该标签与 Interface Builder 中视图中的其他标签一起与约束相连。

- (void)setTeachPieceText:(NSString *)teachPieceText {
      self.teachPieceLabel.text = teachPieceText;
      [self.teachPieceLabel setPreferredMaxLayoutWidth:[self.teachPieceLabel bounds].size.width];
}
于 2014-11-24T21:34:00.240 回答