Autolayout和Frame

现在iOS页面布局用的最多的就是Frame和Autolayout,在Autolayout通过Masonry封装在实际使用中也十分方便。实际上,Autolayout的约束最后都是系统最终转化成frame来进行布局的,对与一个View来说,最终确定其中心点位置和View的宽高。当Autolayout和Frame设置上产生冲突的时候,则会以Autolayout的设置为准。这篇主要讨论布局中常用的几个方法和autolayout遇到动画的情形。

跟布局相关的方法

- (void)setNeedsLayout;
- (void)layoutIfNeeded;
- (void)layoutSubviews;

setNeedsLayout方法标记当前view是需要重新布局的,在下一次runloop中,进行重新布局。如果说想在当前runloop中立刻更新布局,则通过调用layoutIfNeeded方法可以实现,此时系统会调用layoutSubviews。在layoutSubviews方法中,可以自己定义新的view或者改变子view的布局。

Autolayout相关的方法

//view的方法
- (void)updateConstraintsIfNeeded;
//重写view中的方法
- (void)updateConstraints
- (BOOL)needsUpdateConstraints
- (void)setNeedsUpdateConstraints
  
//重写viewController中的方法
- (void)updateViewConstraints

setNeedsUpdateConstraints只是标记当前view的约束需要在下一次runloop中更新,updateConstraintsIfNeeded如果过满足更新条件会立刻调用updateConstraints来更新约束,updateConstraints是子view需要重写的方法,来更新View的约束,最后需要调用[super updateConstraints],否则会崩。而updateViewConstraints是定义在viewController中的,方便去更新viewController对应view的约束。

具体可以通过调用view的setNeedsUpdateConstraints来最终调用到viewController的updateViewConstraints方法来,如果没有这个方法,那么每次都要定义一个子view去重写updateConstraints方法会比较繁琐。updateConstraints和updateViewConstrains方法可以把约束的代码和和业务逻辑分开,另外性能也更好。

为什么会有setxxxxx和xxxifNeeded方法

setxxxxx方法可能是为了性能,没有在代码更新布局或者约束之后立刻执行,而是在下一次runloop中执行。

xxxxIfNeeded方法则是为了在必要的时候,立刻更新约束或者布局,举个例子,有些时候同时使用动画和autolayout。

Autolayout和动画

现在实现一个view滑动的动画:

@interface MyView : UIView
 
-(void)layoutSubviews;
@end
@implementation MyView
 
-(void)layoutSubviews
{
    [super layoutSubviews];
}
@end
- (void)viewDidLoad {
 
    [super viewDidLoad];
 
   
    _container = [[MyView alloc] initWithFrame:self.view.frame];
 
    [self.view addSubview:_container];
 
     
    _redView = [[MyView alloc] initWithFrame:CGRectMake(100, 100, 100, 50)];
 
    _redView.backgroundColor = [UIColor redColor];
 
    [_container addSubview:_redView];
 
    [_redView mas_makeConstraints:^(MASConstraintMaker *make) {
 
        make.centerX.equalTo(self.view);
 
        make.centerY.equalTo(self.view);
 
        make.width.height.equalTo(@100);
 
    }];
 
     
 
    UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
 
    btn.titleLabel.text = @"点击动画";
 
    btn.backgroundColor = [UIColor greenColor];
 
    [_container addSubview:btn];
 
    [btn mas_makeConstraints:^(MASConstraintMaker *make) {
 
        make.centerX.equalTo(self.view);
 
        make.centerY.equalTo(self.view).offset(100);
 
        make.width.equalTo(@100);
 
        make.height.equalTo(@50);
 
    }];
 
    [btn addTarget:self action:@selector(onButtonClick:) forControlEvents:
    UIControlEventTouchUpInside];
 
} 

使用frame

- (void)onButtonClick:(id)sender
 
{
    [UIView animateWithDuration:1 animations:^{
 
        self.redView.frame = CGRectMake(0, self.redView.frame.origin.y, self.
            redView.frame.size.width, self.redView.frame.size.height);
 
    }];
}

和预期结果一致,红色的view滑动到屏幕的最左侧。

使用autolayout

- (void)onButtonClick:(id)sender
 
{
    [UIView animateWithDuration:1 animations:^{
    [self.redView mas_remakeConstraints:^(MASConstraintMaker *make) {
 
        make.width.height.equalTo(@100);
 
        make.left.centerY.equalTo(self.view);
 
    }];
}

和预期的结果不一致,红色的view突然一下移动到屏幕的最左侧,上面这种做法是有问题的,现在在动画的block中添加一行日志的代码。

- (void)onButtonClick:(id)sender
{
    [UIView animateWithDuration:1 animations:^{
        [self.redView mas_remakeConstraints:^(MASConstraintMaker *make) {
            make.width.height.equalTo(@100);
            make.left.centerY.equalTo(self.view);
        }];
        NSLog(@"redView x = %@", @(self.redView.frame.origin.x));
    }];
}

输出日志:

2016-09-07 17:44:03.069 2323[99216:10267632] redView x = 137.5(原来的位置)

说明在重新更新红色View的约束之后,系统并没有立刻转化成对应的frame值,还是原来的位置,但是为什么view会移动到屏幕最右侧而不是静止呢。

因为系统在计算动画插值的过程中,发现红色view的前后的位置是一样的,最后的结果就是在原地静止,猜测有可能系统为了优化直接取消了动画。这是在当前runloop发生的逻辑,在下一次runloop过程中,上一次给红色view设置的约束就生效了,系统会将约束更新到frame的表现上,所以红色view直接跑到了屏幕最左侧。

现在的问题是需要在系统计算动画插值的时候,将视图的frame即时的更新,所以调用红色view的layoutIfNeeded方法就好了。

- (void)onButtonClick:(id)sender
{
    [UIView animateWithDuration:1 animations:^{
        [self.redView mas_remakeConstraints:^(MASConstraintMaker *make) {
 
            make.width.height.equalTo(@100);
 
            make.left.centerY.equalTo(self.view);
 
        }];
 
        NSLog(@"redView x = %@", @(self.redView.frame.origin.x));
 
        [self.container layoutIfNeeded];
 
        NSLog(@"redView x = %@", @(self.redView.frame.origin.x));
 
    }];
}

日志输出为:

2016-09-07 17:50:33.297 2323[99250:10270639] redView x = 137.5
 
2016-09-07 17:50:33.299 2323[99250:10270639] redView x = 0

通过对比可以看到,在调用parentView的layoutIfNeeded之后,其frame得到更新了,所以最后动画如预期一样出现了。这里必须是调用parentView的layoutIfNeeded方法,调用红色视图的layoutIfNeeded方法是不会更新它自己的frame的。

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

推荐阅读更多精彩内容