如果 sizeToFit 依赖于 layoutSubviews,如何实现?

Posted

技术标签:

【中文标题】如果 sizeToFit 依赖于 layoutSubviews,如何实现?【英文标题】:How to implement sizeToFit if it depends on layoutSubviews? 【发布时间】:2015-03-26 19:41:32 【问题描述】:

在layoutSubviews 的文档中,Apple 说:

你不应该直接调用这个方法。

我正在尝试实现sizeToFit。我希望它在所有子视图上放置一个紧密的边界框。在确定这样的边界框之前,我必须布局子视图。这意味着我必须致电layoutSubviews,Apple 对此不以为然。如何在不违反 Apple 规则的情况下解决这个难题?

- (void)layoutSubviews

  [super layoutSubviews];

  self.view0.frame = something;
  self.view1.frame = somethingElse;


- (void)sizeToFit

   [self layoutSubviews];

   self.frame = CGRectMake(
     self.frame.origin.x,
     self.frame.origin.y,
     MAX(
       self.view0.frame.origin.x + self.view0.frame.size.width,
       self.view1.frame.origin.x + self.view1.frame.size.width
     ),
     MAX(
       self.view0.frame.origin.y + self.view0.frame.size.height,
       self.view1.frame.origin.y + self.view1.frame.size.height
     )
  );

【问题讨论】:

请注意,您不应覆盖-sizeToFit。而是实现-sizeThatFits: 【参考方案1】:

不应覆盖-sizeToFit。而是用视图的当前边界大小覆盖-sizeToFit 内部调用的-sizeThatFits:

您不应覆盖此方法。如果要更改视图的默认大小信息,请改写 sizeThatFits:。该方法执行任何需要的计算并将它们返回给该方法,然后该方法进行更改。 – UIView Class Reference


同样,即使您将覆盖-sizeToFit,也很可能没有理由立即执行布局。您只需调整视图的大小,即设置其边界大小。这会触发对-setNeedsLayout 的调用,将视图标记为需要布局。但除非您想为视图设置动画,否则不必立即应用新布局。

这种延迟更新模式的要点是,如果您执行多次连续更新,它会节省大量时间,因为实际更新只执行一次。


我通常这样做。它就像一个魅力。

#pragma mark - Layout & Sizing

- (void)layoutSubviews

    [self calculateHeightForWidth:self.bounds.size.width applyLayout:YES];


- (CGSize)sizeThatFits:(CGSize)size

    CGFloat const width = size.width;
    CGFloat const height = [self calculateHeightForWidth:width applyLayout:NO];

    return CGSizeMake(width, height);


- (CGFloat)calculateHeightForWidth:(CGFloat)width applyLayout:(BOOL)apply

    CGRect const topViewFrame = (
        CGRect frame = CGRectZero;
        ...
        frame;
    );

    CGRect const bottomViewFrame = (
        CGRect frame = CGRectZero;
        ...
        frame;
    );

    if (apply) 
        self.topView.frame = topViewFrame;
        self.bottomView.frame = bottomViewFrame;
    

    return CGRectGetMaxY(bottomViewFrame);

请注意,示例代码适用于可以以任何宽度显示的视图,并且容器会要求特定宽度的首选高度。

不过,您可以轻松地调整其他布局样式的代码。

【讨论】:

【参考方案2】:

我认为在使用自动布局时很少需要使用框架。考虑到您想在不违反 Apple 规则的情况下解决问题,我建议使用 Auto Layout 方式:

设置约束以确定容器视图的大小。

基本上,约束的设置方式使得容器的宽度和高度可以由自动布局系统确定。

例如,您可以设置如下约束:

注意 view0 和 view1 必须同时设置宽度和高度约束。

当您在项目中的某处实例化视图时,您不再需要设置宽度和高度约束。自动布局会根据你之前设置的约束来猜测它的大小(称为 intrinsicContentSize)。

【讨论】:

【参考方案3】:

如果你想强制 layoutSubviews 发生,但又不想直接调用它,设置 needsLayout 标志,然后要求它进行布局。

[self setNeedsLayout];
[self layoutIfNeeded];

【讨论】:

这是一个非常糟糕的主意。调用-sizeThatFits: 绝不应该触发布局更改。不能保证视图稍后会调整到该尺寸,并且您可能会无缘无故地在子视图中移动并移动到无效位置。 -layoutIfNeeded 还会向下传播任何子视图,这可能是立即执行的不必要操作。

以上是关于如果 sizeToFit 依赖于 layoutSubviews,如何实现?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 sizeToFit 会删除我的整个标签?

如何防止 sizeToFit 更改 UILabel 宽度?

具有 sizeToFits() 的标签超出范围

UILabel sizeToFit 在 iOS 8 中崩溃

sizeToFit()使用心得

SizeToFit() 不能与 \n 一起正常工作?