任何人都可以对UIView's
setNeedsLayout
,layoutIfNeeded
和layoutSubviews
方法之间的关系做出明确的解释吗?以及将使用所有三个的示例实现。感谢。
让我感到困惑的是,如果我向自定义视图发送setNeedsLayout
消息,则在此方法为layoutSubviews
之后调用它,然后跳过layoutIfNeeded
。从文档中我可以预期流程为setNeedsLayout
>导致layoutIfNeeded
被调用>导致layoutSubviews
被调用。
答案 0 :(得分:103)
我仍然试图自己解决这个问题,所以如果它包含错误,请怀疑并请原谅我。
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
通常不会在您的子类中被覆盖。当您希望立即布置视图时,您可以调用此方法。 Apple的实现可能如下所示:
-(void)layoutIfNeeded {
if (self._needsLayout) {
UIView *sv = self.superview;
if (sv._needsLayout) {
[sv layoutIfNeeded];
} else {
[self layoutSubviews];
}
}
}
您可以在视图上调用layoutIfNeeded
以强制它(及其必要的超视图)立即布局。
答案 1 :(得分:34)
我想补充一下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
属性时能够为框架更改设置动画,则需要覆盖edgeInsets
设置器,如下所示并调用setNeedsLayout
,然后调用{{1 }}:
layoutIfNeeded
这样,如果执行以下操作,如果更改动画块内的edgeInsets属性,则会对contentView的帧更改进行动画处理。
- (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
}
如果你没有在setEdgeInsets方法中添加对layoutIfNeeded的调用,动画将无效,因为layoutSubviews将在下一个更新周期被调用,这相当于在动画块之外调用它。
如果您只在setEdgeInsets方法中调用layoutIfNeeded,则不会发生任何事情,因为未设置setNeedsLayout标志。