Answers:
我仍在尝试自己解决这个问题,因此请对此表示怀疑,如果其中包含错误,请原谅我。
setNeedsLayout
这是一个简单的方法:它只是在UIView中的某个位置设置了一个标记,将其标记为需要布局。layoutSubviews
在下一次重绘发生之前,这将强制在视图上调用。请注意,由于该autoresizesSubviews
属性,在很多情况下您不需要显式调用此方法。如果已设置(默认情况下为默认值),则对视图框架的任何更改都将导致该视图布局其子视图。
layoutSubviews
是您做所有有趣的事情的方法。如果可以的话,它相当于drawRect
for layout。一个简单的例子可能是:
-(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
执行一个视图以强制将其(以及必要时的其超级视图)布局。
[subview1 layoutSubviews]
。就像一样drawRect
,您不应该直接调用它。但是我不确定setNeedsLayout
在布局阶段调用是否会导致视图在同一布局阶段进行布局,或者是否将视图延迟到下一个视图。很高兴看到真正了解这一切的人的答案...
我想补充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
按如下方法覆盖setter并调用,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
}
这样,如果执行以下操作,并且更改了动画块内的edgeInsets属性,则将对contentView的帧更改进行动画处理。
[UIView animateWithDuration:2 animations:^{
customView.edgeInsets = UIEdgeInsetsMake(45, 17, 18, 34);
}];
如果未在setEdgeInsets方法中添加对layoutIfNeeded的调用,则该动画将无法工作,因为layoutSubviews将在下一个更新周期被调用,这等同于在动画块之外调用它。
如果仅在setEdgeInsets方法中调用layoutIfNeeded,则不会发生任何事情,因为未设置setNeedsLayout标志。
[self layoutIfNeeded]
在设置边缘插入后在动画块内调用即可。