当自动布局遇到ScrollView

<p>2012年,Auto Layout作为iOS6版本的一部分,首次在iOS中露面。发展至今,Auto Layout已经成为了iOS中一种重要的布局技术。然而,每个版本的iOS中,自动布局的实现都会有小小的改进,这种改进,或者说是差异,对于开发者来说既是好消息,又是坏消息。一方面这个技术在不断的变得好用和更加完善,另一方面对于需要兼容到老版本的开发者来说,这又意味着许多额外的坑需要去踩。</p>
<p> 有很多文章介绍了该如何使用autolayout,并且针对原生Autolayout不好用的接口,也有不少第三方的封装。然而,当在自动布局中应用scrollview的时候,总有些让人不爽的地方。明明在iOS8上跑的好好的界面,放到iOS7上就可能有各种问题,不是显示不全,就是突然滑不动了。如果还要兼容iOS6的系统,简直就直接想换成frame布局算了,好在用iOS6的人确实不多了。
</p>
<p>关于这个问题,有篇博文(<a href="http://adad184.com/2015/12/01/scrollview-under-autolayout/">UIScrollview与Autolayout的那点事</a>)讲的很清楚,它给的<a href="https://github.com/adad184/DemoScrollViewAutolayout">例子</a>也恰到好处的说明了问题的本质:
自动布局中,UIScrollView 依靠与其subviews之间的约束来确定ContentSize的大小!
上面这个概念很重要,理解了上面那句话,我们才能在自动布局中应用好UIScrollView;如何来理解呢?UIScrollView是个非常特殊的view,UIScrollView与其subview之间相对位置的约束 并不会直接用于frame的计算,而是会转化为对ContentSize的计算。换句话说,当UIScrollView知道了上下左右的约束分别指向subview什么位置之后,只要subview的位置固定下来了 ContentSize的大小就确定下来了。
<br />
第一段代码:
<code>
[scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
make.eadges.equalTo(v1);
}];
</code>
上面代码只是利用masonry来确定了scrollView的frame,它等同于其子view的v1,然而这并不够,我们知道UIScrollView除了需要指定frame之外,还需要指定ContentSize。
<br />
通过添加于scrollView的子view来确定ContentSize:
第二段代码:
<code>
[subView1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(scrollView);
make.width.equalTo(scrollView);
make.height.equalTo(scrollView).multipliedBy(1.5);}
];
</code>
初学自动布局的人可能以为上面的代码这对subView1的约束有点多余,既然指定了eadges,为何还要指定width和height?岂不是会有约束冲突?
然而这里并没有约束冲突。指定了eadges只是确定了subView1的边界将是scrollView内容页边界(这样理解可能会更清楚些,虽然代码写的是scrollView,但实际效果确实是scrollView的内容页),而并非scrollView自己的Frame。那么再指定width和height,同样确定的是scrollView内容页的宽和高。
对于上面的代码,如果我们去掉关于width和height的约束,则scrollView还可以展示出来,原因是第一段代码中我们指定来scrollview相对v1的约束,即确定了scrollView的显示窗口。不过contentSize却没法计算,会为(0,0)。因为我们指定了subView1相对于contentView的约束,而subView1并未给出具体的宽和高。但是contentView的宽和高需要根据其子View来计算,如果subView1是ScrollView的唯一子View的话,那么具体ContentSize是无法计算的,因为subView1并未提供。
“<a href="http://adad184.com/2015/12/01/scrollview-under-autolayout/">UIScrollview与Autolayout的那点事</a>”很好的介绍了单个UIScrollView如何确定它的contentSize,仔细阅读它的<a href="https://github.com/adad184/DemoScrollViewAutolayout">demo</a>会有更多的收获。
<br />
然而,我们的应用还会有很多更复杂的场景,通常会有多个UIScrollView在同一个界面一起使用的情况,其中横向的scrollView可以横向翻页,而对于每一页,又有纵向的scrollView(通常是UITableView)可以纵向翻页。
场景虽然复杂了些,然而问题的本质还是没有变化,scrollView的contentSize将由其子View来进行计算!
下面的代码中给出了一个主scrollView嵌套另一个子scrollView的情况。子scrollView又可以带上tabview等多个子视图。这是一种常见的布局场景。
<code>
CGFloat width = [[UIScreen mainScreen] bounds].size.width;
CGFloat height = [[UIScreen mainScreen] bounds].size.height;

UIScrollView *scrollView = [UIScrollView new];
scrollView.backgroundColor = [UIColor lightGrayColor];
scrollView.clipsToBounds   = NO;
scrollView.bounces  = YES;
scrollView.delegate = self;
scrollView.tag      = 1;
self.scrollView = scrollView;

[self.view addSubview:scrollView];
[scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.edges.equalTo(self.view);
}];

UIView* headView = [[UIView alloc] init];
[scrollView addSubview:headView];
[headView setBackgroundColor:[UIColor redColor]];

UIView* tabView = [UIView new];
[scrollView addSubview:tabView];
[tabView setBackgroundColor:[UIColor yellowColor]];

//subviews
UIScrollView* subScrollView = [UIScrollView new];
[scrollView addSubview:subScrollView];
subScrollView.bounces       = NO;
subScrollView.clipsToBounds = NO;
subScrollView.pagingEnabled = YES;
subScrollView.tag           = 2;
self.subScrollView = subScrollView;

[headView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.left.right.top.equalTo(scrollView);
    make.width.mas_equalTo(width);
    make.height.mas_equalTo(108);
}];

[tabView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.height.mas_equalTo(44);
    make.top.equalTo(headView.mas_bottom);
    make.left.right.equalTo(headView);
}];

CGFloat subHeight = height-44-49;
[subScrollView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.equalTo(tabView.mas_bottom);
    make.left.right.equalTo(tabView);
    make.height.mas_equalTo(subHeight);
    make.bottom.equalTo(scrollView).offset(-49);
}];

UIView* greenView = [UIView new];
[subScrollView addSubview:greenView];
greenView.backgroundColor = [UIColor greenColor];

UITableView* tableView= [[UITableView alloc] init];
[subScrollView addSubview:tableView];
tableView.delegate   = self;
tableView.dataSource = self;
tableView.tag        = 3;
self.tabTable        = tableView;
[tableView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.left.top.equalTo(subScrollView);
    make.width.mas_equalTo(width);
    make.height.mas_equalTo(subHeight);
}];

[greenView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.left.equalTo(tableView.mas_right);
    make.width.height.top.equalTo(tableView);
    make.right.bottom.equalTo(subScrollView);
}];

</code>

在确定subScrollView的高度时,使用确定的数字能让结果比较确定,上述代码中使用的是
CGFloat subHeight = height-44-49;
44是tabView的高度,而49是底下tabView的高度。
效果如图:


123.png

这个例子是将上面提到的文章中的第一个例子做了改造而来,具体加入了对付多个UIScrollView的情况,至此,如果你彻底明白了上面的例子,那么自动布局应用在UIScrollView也就没有什么问题了。

当多个UIScrollView在一起的时候,滑动时哪个UIScrollView优先响应手势事件就需要一种机制来指定。通过scrollDidScroll方法可以确定具体是谁该滑动。
<code>

  • (void)scrollViewDidScroll:(UIScrollView *)scrollView
    {
    if(_scrollView.contentOffset.y + _scrollView.frame.size.height < _scrollView.contentSize.height) {
    _tabTable.scrollEnabled = NO;
    }
    else {
    _tabTable.scrollEnabled = YES;
    }
    }
    </code>
    上述逻辑指定主scrollView先滑动,当滑倒底之后,才让table里面的cell可滑。

最后,即便如此,在布局完毕之后,如果你的应用需要支持iOS7,那还是要用对应的系统多跑一跑,没有问题才好。有问题的话,看下对应scrollView的contentSize是否正确,各个scrollView的子View是否可以确定对应的contentView(可以理解为隐藏属性)了。
<br />
在复杂的view带有scrollView的布局中,如果要支持iOS7,最好还是用纯frame布局吧,至少在这种情况下,兼容问题不用再花费额外的时间去处理。
</p>

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

推荐阅读更多精彩内容