15

我有一个似乎有效的解决方案,但我想确保它是安全的并按预期使用该工具。我可以将我的问题归结为这种情况 - 我有一个 UIView 包含多个 UILabel,垂直堆叠。每个标签都可以填充垂直环绕和扩展标签的任意文本。在视图中堆叠标签的自动布局约束是微不足道的。我想做的是让 UIView 将其首选高度(视图的高度应该适合所有标签)通过intrinsicContentSize. 但是,内部布局需要解决,才能返回合适的高度。我的理解是求解器在从返回后保证是完整的[super layoutSubviews],这就是我正在做的事情:

- (void)layoutSubviews
{
    [super layoutSubviews];

    // solver's complete, now we can measure?
    [self invalidateIntrinsicContentSize];
}

我的intrinsicContentSize实现是指最后一个标签的框架来计算高度。这一切似乎都适用于我目前的情况,但这让我感到紧张。似乎求解器在布置其内部时要求当前视图的 intrinsicContentSize 。我不会详细介绍,但我遇到了这种模式陷入无限循环的情况。我可以通过调整我的一些布局代码来解决这个问题——这并没有激发信心。

有一个更好的方法吗?这似乎是一件很常见的事情,当我开始解决这个问题时,我认为这就是 intrinsicContentSize 的用途。我见过的 intrinsicContentSize 的实现一直都是微不足道的——硬编码维度的大小,或者测量不依赖于已解决的内部布局的单个视图。

注意,我不能只通过NSString sizeWith...方法来测量标签,因为内部布局太复杂了。

请注意,我探索了 using systemLayoutSizeFittingSize:,但从 intrinsicContentSize 调用它会导致无限循环。

任何回应将不胜感激!提前致谢!

4

2 回答 2

7

我想更新这个线程并结束这个问题 - 我认为对于其他刚开始使用 AutoLayout 可能有类似误解的人来说,这里仍然有价值。了解intrinsicContentSize 的真正目的很重要——这不是典型视图将其首选大小传达给其父视图的方式。如果一个视图使用 AutoLayout 来布局其子视图,那么它将使用定义其内部布局的约束来通知其父视图其大小 - 具体而言,它将是将子视图与其父视图相关联的约束,这将有助于定义父母的大小。这实际上是 AutoLayout 中既神奇又令人沮丧的部分——它是导致布局的所有这些约束(视图内部和外部)的总和。

我认为与多行文本相关的早期 iOS 错误/不一致是导致我走上intrinsicContentSize 之路的原因。多行文本仍然不完美,但希望在 iOS10 中更好。

于 2016-09-26T23:12:30.457 回答
3

这是我为这个问题提出的解决方案。如果您想提供一个封装其他实例的intrinsicContentSizefor a ,那么有两种可能性。要么所有子视图都有值,你可以从这些值中工作,否则在某处至少有一个值,你需要依赖这些子视图的布局结果来计算你的.NSViewNSViewintrinsicContentSizeNSViewNoInstrinsicMetricintrinsicContentSize

在第二种情况下,您intrinsicContentSize取决于其子视图的框架值,您需要对这些框架的任何更改做出反应,就像标签对其包含的文本的任何更改做出反应一样。您可以使用NSViewFrameDidChangeNotification. 这是我在自定义NSView子类中的做法:

- (void)didAddSubview:(NSView *)subview
{
    [super didAddSubview:subview];
    subview.postsFrameChangedNotifications = YES;
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(subviewFrameDidChange:) name:NSViewFrameDidChangeNotification object:subview];
    [self setNeedsUpdateConstraints:YES];
    [self invalidateIntrinsicContentSize];
}

- (void)willRemoveSubview:(NSView *)subview
{
    [super willRemoveSubview:subview];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:NSViewFrameDidChangeNotification object:subview];
    [self setNeedsUpdateConstraints:YES];
    [self invalidateIntrinsicContentSize];
}

- (void)subviewFrameDidChange:(NSNotification*)notif
{
    [self invalidateIntrinsicContentSize];
}
于 2014-09-13T00:12:10.873 回答