UIView的setNeedsLayout,layoutIfNeeded和layoutSubviews之间有什么关系?

任何人都可以给UIView's setNeedsLayoutlayoutIfNeededlayoutSubviews方法之间的关系明确的解释? 以及所有三个将被使用的示例实现。 谢谢。

让我感到困惑的是,如果我发送自定义视图setNeedsLayout消息,那么它在layoutSubviews之后调用的下一个东西将跳过layoutIfNeeded 。 从文档我希望stream程setNeedsLayout >导致layoutIfNeeded调用>导致layoutSubviews被调用。

我仍然试图自己解决这个问题,所以采取这个怀疑主义,并原谅我,如果它包含错误。

setNeedsLayout是一个简单的方法:它只是在UIView的某个地方设置一个标志,将其标记为需要布局。 这将强制layoutSubviews在下一次重绘之前在视图上被调用。 请注意,在许多情况下,由于autoresizesSubviews属性,您不需要明确地调用它。 如果这是设置的(默认情况下),那么对视图框架的任何改变都将导致视图布置其子视图。

layoutSubviews是你做所有有趣的东西的方法。 如果你愿意的话,它相当于布局的drawRect 。 一个微不足道的例子可能是:

 -(void)layoutSubviews { // Child's frame is always equal to our bounds inset by 8px self.subview1.frame = CGRectInset(self.bounds, 8.0, 8.0); // It seems likely that this is incorrect: // [self.subview1 layoutSubviews]; // ... and this is correct: [self.subview1 setNeedsLayout]; // but I don't claim to know definitively. } 

AFAIK layoutIfNeeded通常不会在您的子类中被覆盖。 当你想要现在看到一个视图时,这是一个你打算打电话的方法。 苹果的实现可能看起来像这样:

 -(void)layoutIfNeeded { if (self._needsLayout) { UIView *sv = self.superview; if (sv._needsLayout) { [sv layoutIfNeeded]; } else { [self layoutSubviews]; } } } 

你可以在视图上调用layoutIfNeeded来强制它(及其必要的超级视图)被立即布置。

我想添加n8gray的答案,在某些情况下,您将需要调用setNeedsLayout后跟layoutIfNeeded

比方说,您编写了一个扩展UIView的自定义视图,其中子视图的位置是复杂的,不能用autoresizingMask或iOS6 AutoLayout完成。 自定义定位可以通过覆盖layoutSubviews来完成。

作为一个例子,假设你有一个自定义的视图,它有一个contentView属性和一个edgeInsets属性,允许设置contentView的边距。 layoutSubviews将如下所示:

 - (void) layoutSubviews { self.contentView.frame = CGRectMake( self.bounds.origin.x + self.edgeInsets.left, self.bounds.origin.y + self.edgeInsets.top, self.bounds.size.width - self.edgeInsets.left - self.edgeInsets.right, self.bounds.size.height - self.edgeInsets.top - self.edgeInsets.bottom); } 

如果您希望在更改edgeInsets属性时能够更改框架的animationedgeInsets ,则需要按如下方式覆盖edgeInsets设置器,然后调用setNeedsLayoutlayoutIfNeeded

 - (void) setEdgeInsets:(UIEdgeInsets)edgeInsets { _edgeInsets = edgeInsets; [self setNeedsLayout]; //Indicates that the view needs to be laid out //at next update or at next call of layoutIfNeeded, //whichever comes first [self layoutIfNeeded]; //Calls layoutSubviews if flag is set } 

这样,如果您执行以下操作,如果您更改animation块内的edgeInsets属性,则contentView的帧更改将会变为animation。

 [UIView animateWithDuration:2 animations:^{ customView.edgeInsets = UIEdgeInsetsMake(45, 17, 18, 34); }]; 

如果不在setEdgeInsets方法中添加对layoutIfNeeded的调用,则animation将无法工作,因为layoutSubviews将在下一个更新周期被调用,这相当于将其调用到animation块之外。

如果只调用setEdgeInsets方法中的layoutIfNeeded,则不会设置setNeedsLayout标志。