UIView的setNeedsLayout,layoutIfNeeded和layoutSubviews之间有什么关系?
任何人都可以给UIView's
setNeedsLayout
, layoutIfNeeded
和layoutSubviews
方法之间的关系明确的解释? 以及所有三个将被使用的示例实现。 谢谢。
让我感到困惑的是,如果我发送自定义视图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
设置器,然后调用setNeedsLayout
和layoutIfNeeded
:
- (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标志。