layoutIfNeeded和layoutSubviews
一般控件设置好约束后,不会立即转化成frame,而是被标记等待下一个runloop运行周期刷新,因此我们这时想直接取到控件的宽高为0
[headView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.offset(100);
make.left.offset(100);
make.right.offset(-100);
make.height.equalTo(@200);
}];
NSLog(@"宽:%f,高:%f",headView.frame.size.width,headView.frame.size.height);
[headView layoutIfNeeded];
NSLog(@"宽:%f,高:%f",headView.frame.size.width,headView.frame.size.height);
- 调用layoutIfNeeded就会立即刷新frame后就可以获取到frame了。
1、layoutIfNeeded后一定能取到控件的大小吗?
不一定,layoutIfNeeded就会立即刷新frame,但是前提条件是父控件确定好外部约束了或者父控件的大小不能为0,否则控件即使刷新了frame也不能确定自身的frame。
UIView *view = [[UIView alloc]init];
view.backgroundColor =[UIColor greenColor];
[headView addSubview:view];
[view mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.top.bottom.equalTo(headView);
}];
[view layoutIfNeeded];
NSLog(@"%f",view.frame.size.height);
[headView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.offset(100);
make.left.offset(100);
make.right.offset(-100);
make.height.equalTo(@200);
}];
[view layoutIfNeeded];
NSLog(@"%f",view.frame.size.width);
这样或许我们会在开发中就会遇到了这样的问题,明明我也调用了layoutIfNeeded,但是还是为0。
比如:
@interface CourseDetailHeadView()
@end
@implementation CourseDetailHeadView
- (instancetype)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
UIView *view = [[UIView alloc]init];
[self addSubview:view];
view.backgroundColor = [UIColor greenColor];
[view mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.top.offset(10);
make.right.offset(-20);
make.bottom.offset(-100);
}];
[view layoutIfNeeded];
NSLog(@"%f",view.frame.size.width);
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
CourseDetailHeadView *headView = [[CourseDetailHeadView alloc]init];
[self.view addSubview:headView];
self.headView = headView;
headView.backgroundColor = [UIColor redColor];
[headView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.top.offset(100);
make.right.bottom.offset(-100);
}];
}
明明我们调用了[view layoutIfNeeded]方法,却得到的view宽度还是0,why?因为CourseDetailHeadView在初始化时,还无法确定父控件的位置约束 ,更不用说确定子控件view大小了,毕竟view的约束是依赖父控件的。
- (void)viewDidLoad {
[super viewDidLoad];
CourseDetailHeadView *headView = [[CourseDetailHeadView alloc]initWithFrame:CGRectMake(10, 100, 300,100)];
[self.view addSubview:headView];
self.headView = headView;
headView.backgroundColor = [UIColor redColor];
}
- (instancetype)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
UIView *view = [[UIView alloc]init];
view.backgroundColor =[UIColor greenColor];
[self addSubview:view];
[view mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.top.bottom.equalTo(self);
}];
[view layoutIfNeeded];
NSLog(@"%f",view.frame.size.width);
}
return self;
}
像这样在初始化的同时并确定好frame,这样[view layoutIfNeeded]就可以得到此时真实的frame
2、layoutSubviews什么时候调用?
layoutSubviews默认实现不做任何事情,可以用来确定子控件的frame,但你不应该直接调用该方法,如果您想强制进行布局更新,请在下一次绘图更新之前调用setNeedsLayout方法。如果您想立即更新视图的布局,请调用layoutIfNeeded方法。
什么时候调用请参考:https://www.jianshu.com/p/e6e04eb8c21d
2.1、setNeedsLayout,
- 这个可以对视图进行标记,标记为需要布局更新,但是不会立即更新,而是会等待下一个运行循环才会去更新。
- (void)viewDidLoad {
[super viewDidLoad];
CourseDetailHeadView *headView = [[CourseDetailHeadView alloc]initWithFrame:CGRectMake(10, 100, 300,100)];
[self.view addSubview:headView];
self.headView = headView;
headView.backgroundColor = [UIColor redColor];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self.headView setNeedsLayout];
}
@implementation CourseDetailHeadView
- (void)layoutSubviews{
[super layoutSubviews];
NSLog(@"布局子控件");
}
- 对视图进行标记后,待下一个运行循环才会去更新,然后就会触发layoutSubviews的调用。
- 直接调用setLayoutSubviews,一定会触发layoutSubviews的调用
2.2、layoutIfNeeded
- 如果有需要刷新的标记,立即调用layoutSubviews进行布局
- (void)viewDidLoad {
[super viewDidLoad];
CourseDetailHeadView *headView = [[CourseDetailHeadView alloc]initWithFrame:CGRectMake(10, 100, 300,100)];
[self.view addSubview:headView];
self.headView = headView;
headView.backgroundColor = [UIColor redColor];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self.headView layoutIfNeeded];
}
-调用 layoutIfNeeded不一定会调用layoutSubviews, layoutIfNeeded顾名思义:需要刷新时布局 ,才会立即刷新,视图只有被系统或者自己标记,才会立即刷新。而这个例子中因为没有被系统标记过,自己也没标记过,不会去刷新布局,因此不会即使调用了 layoutIfNeeded也不会触发layoutSubviews。
如果你想手动去标记,并且立即再触发layoutSubviews,你可以这样。
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self.headView setNeedsLayout];
[self.headView layoutIfNeeded];
}