iOS layoutSubviews

layoutSubviews 是UIView里面的一个方法,不可以直接调用,属于被动调用的,如果要看这个方法的调用时机,只能是建一个UIView的子类,在子类里面重写layoutSubviews方法。
从其他博客上看到了它的调用时机总结,如下:

  • init初始化不会触发layoutSubviews
  • 当view的fram的值为0的时候,addSubview不会调用layoutSubviews
  • addSubview的时候。
  • 当view的frame发生改变的时候。
  • 直接调用setNeedsLayout方法会调用。
  • 滑动UIScrollView的时候。
  • 旋转Screen会触发父UIView上的layoutSubviews事件。
  • 改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件。

下面在项目里面一条一条验证下。在新建项目里面随便创建一个UIView的子类WeekView,在子类重写layoutSubviews

-(void)layoutSubviews{
    [super layoutSubviews];
    NSLog(@"执行的方法名:layoutSubviews");
}

在ViewController中引入头文件

#import "WeekView.h"

初始化一个WeekView对象,先不添加到控制器的view上

- (void)viewDidLoad {
    [super viewDidLoad];
    WeekView *weekView = [[WeekView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
}

运行一下,看输出台会发现什么也没有。说明init的时候不会触发layoutSubviews方法
然后将对象weekView添加到控制器的view上

- (void)viewDidLoad {
    [super viewDidLoad];
    WeekView *weekView = [[WeekView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
    [self.view addSubview:weekView];
}

运行一下,看控制台,如下图:


可以看到layoutSubviews方法被执行了一次,说明addSubview的时候会出发layoutSubviews方法

将weekView的fram设置为0

    WeekView *weekView = [[WeekView alloc] initWithFrame:CGRectZero];

运行一下,发现打印台没有打印任何东西,说明当view的fram的值为0的时候,addSubview不会调用layoutSubviews

下面检验一下当view的fram发生改变的时候是否会调用layoutSubviews。给ViewController添加一个属性

@property(nonatomic,strong)WeekView * weekV;

把之前创建的对象赋值给该属性

    self.weekV = weekView;

在touchesBegan的方法中改变weekV的fram

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    self.weekV.frame = CGRectMake(0, 100, 100, 100);
}

运行一下,并在屏幕上进行点击操作。控制台打印结果如下:



发现,打印结果和先前并无不同,说明fram改变了,但是没有触发layoutSubviews。但是我改变的只是fram的坐标,即x的值,没有改变大小,那下面改变大小不改变坐标试试

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    self.weekV.frame = CGRectMake(100, 100, 200, 100);
}

运行并点击屏幕,控制台打印结果如下


发现打印了两次,也就是改变fram的大小会触发layoutSubviews,改变frame的x,y的值并不会触发

到这一步,调用时机总结中的八条,其实已经验证五条(前四条和最后一条)。
下面来看第五条直接调用setNeedsLayout方法

setNeedsLayout方法的意思是: 将调用者标记为需要重新布局,然后会异步调用layoutIfNeeded刷新布局,既然异步就不立即刷新,但layoutSubviews一定会被调用
layoutIfNeeded的意思是:当我被调用且被调用者有需要刷新的标记,则立即调用被调用者的layoutSubviews方法进行布局,如果被调用者没有被标记,layoutSubviews是不会被调用的。
另外就是,如果要立即刷新,要先调用[view setNeedsLayout],把标记设为需要布局,然后马上调用[view layoutIfNeeded],实现刷新布局。现在验证下
在ViewController的touchesBegan方法中添加如下代码

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [self.weekV setNeedsLayout];
    NSLog(@"点击了屏幕");
}

运行一下,先不点击屏幕,打印结果如下


打印结果没毛病,然后点击屏幕,结果如下

发现点击屏幕走了一遍layoutSubviews的方法。
那说明直接调用setNeedsLayout会触发layoutSubviews方法
另外从打印结果中可以看出,layoutSubviews方法是被异步执行的,因为代码中NSLog(@"点击了屏幕")我是写在[self.weekV setNeedsLayout]下面的,但是打印的结果却是打印“点击了屏幕”在前,执行layoutSubviews在后。说明直接调用setNeedsLayout,会异步执行layoutSubviews
那如果我调用setNeedsLayout后,然后立马调用layoutIfNeeded,会怎样。在touchesBegan方法中添加代码 [self.weekV layoutIfNeeded],修改如下:

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [self.weekV setNeedsLayout];
    [self.weekV layoutIfNeeded];
    NSLog(@"点击了屏幕");
}

运行一下,然后点击屏幕,打印结果如下:


从打印的结果可以很明显的看出在setNeedsLayout之后,直接调用layoutIfNeeded,会立即执行layoutSubviews方法,然后才执行后面的代码。
那如果我们在点击事件中,不调用setNeedsLayout方法,而是直接调用layoutIfNeeded会怎样呢?按照我们之前的分析,layoutSubviews将不会执行,验证下。
代码如下

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//    [self.weekView setNeedsLayout];
    [self.weekView layoutIfNeeded];
    NSLog(@"我点击了屏幕");
}

运行工程,多次点击屏幕,打印结果如下:


可以确定仅仅是调用layoutIfNeeded不会调用layoutSubviews方法的

下面验证滑动UIScrollView是否会调用layoutSubviews
创建一个UIScrollView的子类WeekScrollView,在WeekScrollView.m文件中重写layoutSubviews方法,如下:

-(void)layoutSubviews{
    NSLog(@"调用了scrollView的setNeedsLayout方法");
}

在ViewController.m中引入头文件

#import "WeekScrollView.h"

添加属性

@property(nonatomic,strong)WeekScrollView * weekScrollV;

初始化,并添加到控制器的view上

    //创建滑动视图
    self.weekScrollV = [[WeekScrollView alloc] initWithFrame:CGRectMake(0, 200, 250, 250)];
    self.weekScrollV.contentSize = CGSizeMake(250, 500);
    self.weekScrollV.backgroundColor = [UIColor blueColor];
    [self.view addSubview:self.weekScrollV];

运行一下,先不滑动视图,打印结果如下


这个打印结果,没毛病,因为两次调用addSubview的方法。来看滑动视图后的结果,如下:
WX20170417-100029@2x.png

发现只要滑动scrollView视图,会密集的调用其父类的layoutSubviews方法

最后验证旋转Screen是否会调用layoutSubviews。这就需要用到真机了。
先运行下项目,先不旋转手机,控制台打印如下


会发现scrollView 的layoutSubviews触发了两遍,但刚刚在模拟器上只调用一遍layoutSubviews。这个问题可描述成这样:拿真机做测试时,凡初始化UIScrollView的操作会调用两遍layoutSubviews,但是对于UIView却只调用一遍;而对于模拟器,两者都只是触发一遍layoutSubviews。这个问题先放一边。继续测试,先旋转手机试试,第一次旋转,打印结果如下

第二次旋转,打印结果如下

这样我们可以看出屏幕旋转,UIScrollView的layoutSubviews会被触发,而且每次旋转都是触发两次layoutSubviews方法;但是UIView的layoutSubviews却不会被触发
为了验证一下是不是只有UIScrollView旋转才触发,我再创建一个UIButton的子类WeekButton,创建过程就略去了,在ViewController中初始化weekB,如下

    //初始化按钮
    self.weekB = [[WeekButton alloc] initWithFrame:CGRectMake(0, 450, 40, 40)];
    self.weekB.backgroundColor = [UIColor brownColor];
    [self.view addSubview:self.weekB];

在WeekButton.m中重写layoutSubviews方法

-(void)layoutSubviews{
    NSLog(@"执行了WeekButton的layoutSubviews方法");
}

运行一下,先不旋转,结果如下



可以看到WeekScrollView和weekButton的layoutSubviews方法都运行了两次,先不管这个两次。然后旋转手机一次,运行结果如下

可以看到WeekScrollView和weekButton的layoutSubviews方法都运行了两次,但是WeekView的layoutSubviews方法没有运行
这样到这一步,我们就可以总结出两条:
旋转屏幕不触发UIView的layoutSubviews 方法,但会触发UIButton,UIScrollView等控件的layoutSubviews 方法
在真机上运行,初始化或者旋转屏幕,UIButton,UIScrollView等控件的子类会触发两次layoutSubviews 方法
问题也有两个了:
为什么是两次触发layoutSubviews 方法?
为什么旋转屏幕不触发UIView 的layoutSubviews 方法?
暂时找不到答案。以后搞懂了再来回答

后面我又试了下在真机上运行项目,改变UIScrollView的frame的大小,只运行了一次layoutSubviews。

综上再综上,layoutSubviews方法因为运行次数不靠谱,而且旋转屏幕不是所有的控件都会调用该方法,所有不适合在该方法中初始化视图,但是可以在该方法中刷新视图,尤其是对子视图进行重新布局。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,193评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,306评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,130评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,110评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,118评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,085评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,007评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,844评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,283评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,508评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,395评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,985评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,630评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,797评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,653评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,553评论 2 352

推荐阅读更多精彩内容