两个 AutoLayout 的小建议

两个 AutoLayout 的小建议


一些值得记录的 AutoLayout 用法。


Tip 1:两个不等宽的 View,彼此相邻,并“共同”居中于 Superview


文字可能不好描述,那就来图片:


如上图所示,黑色的是 Superview,它有两个 Subview,一个是 imageView,一个是 label。


imageView 和 label 相邻,且“它们的组合”居中于 Superview。label 的宽度是可变的,左右两个示意图非常清楚。


那怎样用 AutoLayout 的约束来描述这样的布局呢?


首先来处理两个 Subview 的“相邻”:


NSArray *constraintH = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(>=0)-[imageView]-[label]-(>=0)-|"

                                                                   options:0

                                                                   metrics:nil

                                                                     views:viewsDictionary];


这个约束集描述了 imageView 和 label 相邻,而且,imageView 的左边相对于 Superview 的距离 >=0 ,即是可变的;同样,label 的右边相对于 Superview 的距离也 >= 0,一样可变。但若此时运行代码,它们并不会居中于 Superview,可能都挤在右边或左边。


我们使用一个 helperView 来处理“居中”问题。


NSLayoutConstraint *constraint3 =[NSLayoutConstraint constraintWithItem:helperView

                                                              attribute:NSLayoutAttributeLeft

                                                              relatedBy:NSLayoutRelationEqual

                                                                 toItem:self.imageView

                                                              attribute:NSLayoutAttributeLeft

                                                             multiplier:1

                                                               constant:0];

 

NSLayoutConstraint *constraint4 =[NSLayoutConstraint constraintWithItem:helperView

                                                              attribute:NSLayoutAttributeRight

                                                              relatedBy:NSLayoutRelationEqual

                                                                 toItem:self.label

                                                              attribute:NSLayoutAttributeRight

                                                             multiplier:1

                                                               constant:0];


helperView 的左边相对于 imageView 的左边对齐,helperView 的右边相对于 label 的右边对齐。最后重点来了:


NSLayoutConstraint *constraint5 =[NSLayoutConstraint constraintWithItem:helperView

                                                              attribute:NSLayoutAttributeCenterX

                                                              relatedBy:NSLayoutRelationEqual

                                                                 toItem:self.view // Superview

                                                              attribute:NSLayoutAttributeCenterX

                                                             multiplier:1

                                                               constant:0];


我们让 helperView 的 X 中心与 Superview 的 X 中心一致。于是,伟大的约束会让 helperView 横向居中于 Superview,虽然我们看不见它,但它会将 imageView 和 label 拉到“共同”居中的位置。


搞定!(注意上面的代码省略了一些竖向居中的约束。)虽然是用代码描述的,但是文字说明很清楚了,直接在 IB 里构建约束也没有问题。关键就是我们的 helperView,它虽不可见,但只要它的约束起作用就可以了。


我也写了一个 Demo 放在 GitHub,如有必要,请稍微看看!


Tip 2:让 AutoLayout 与 UIScrollView 合作无间


只要度过了最开始的不适应期,各位用着 AutoLayout 时应该都是心情愉悦的。虽然手写约束会给人冗长的感觉,但 API 的长度并不会阻碍你对代码的理解。不过大部分时间里,我们都在 Storyboard 里,愉快的链接着约束,日子美好又幸福。


可惜好景不长。有一天,我们遇到的某个需求很可能需要我们用 UIScrollView 来实现。比如一个很长的展示页面,里面有文字、有图片、可能还有一些按钮等等。它们的长度或宽度很可能会超过 iPhone(或 iPad)的高度或宽度。Apple 当然很仔细地研究过小尺寸屏幕,它提供的解决方案就是 UIScrollView。


我们就来试试。


我们拖入一个 UIScrollView 放在 ViewController 的 View 里,并设置其约束,到四边的距离都是0:


更新一下它的 Frame:


然后我们拖入一个 UILabel 到 UIScrollView 里,这时 Storyboard 就会报告两个错误:




如果英文不太好的话,我们可以用谷歌翻译。ambiguous 的意思是“不明确”,也就是说,UIScrollView 不能确定其内容的宽或高。


由此,我们可以推断,当 UIScrollView 有 SubView 的时候,它就会开始考虑其“内部”的 contentView 的 Size 了。因为 UIScrollView 处于一个 AutoLayout 的环境中,它不能直接得到 SubView 的 Frame,也就不能确定其 contentView 的 Size,于是 Storyboard 就会跑出来告诉我们这件事。


那我们给 UILabel 加上 3 边约束:


正常来说,只需要上边和左边就能确定 UILabel 的位置,但右边的约束的作用实际是“撑宽”UIScrollView,这时错误就只有一个了:


很明显,UIScrollView 可以确定其 contentView 的宽度了,因为 UILabel 的宽度固定,它的左边到 UIScrollView 的左边固定,它的右边到 UIScrollView 的右边固定,于是 AutoLayout 系统可以通过这些约束“猜出” UIScrollView 的 contentView 的宽度。


然后,我们再给 UILabel 加上下边的约束,错误就会完全消失了。


可事情还没完。若我想 UILabel 的右边约束是 20,下边约束也是 20,可行吗?读者可以自行修改约束的值,很明显是可以的。但这时候就“目测”来看,很明显这两个约束的“线段”会长于 20,那为什么 Storyboard 不会报错或警告呢?


如果读者理解了前面的过程,那就会有答案。因为 UILabel 在 UIScrollView 内的 contentView 上,虽然看起来 UIScrollView 很宽很大,但其 contentView 并不是。相反,contentView 的 Size 是由其中的 Subview 的约束所确定的。


这就是你在同时使用 AutoLayout 和 UIScrollView 时所唯一需要明白的地方。


实际上,我们可以更进一步,修改 ViewController 的尺寸。


将 ViewController 的 Size 改为 Freeform:



并将 View 的 Size 改为 (70, 69)



这时候就看起来“正常”了,对吧?同样的道理,如果你要往 UIScrollView 里放入很多很多的 Subview,那你就先将 View 的 Size 改到一个合适的尺寸,再做 Subview 的布局,注意修改约束的“值”(因为看起来的长度不一定是实际的长度)。


最后强调一点,确保约束能让 AutoLayout 确定 UIScrollView 的 contentView 的 Size,此外就是正常的 AutoLayout 用法。当然,有时候我们只需要高度增加,宽度和屏幕一样,那约束好设置吗?


同样有个 Demo 放在 GitHub,如有必要,请稍微看看!


2015年11月7日补记:微博评论里有人提到所谓正确的用法是“使用一个单一的containerView占满全部,然后把所有的subview添加到containerView中”,但这是我极力避免的,因为这样的方式并没有说清楚原理。Demo 里有三个 Tab,第一个是上面介绍的过程,第二个是限制为只能上下滑动,第三个是要显示


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值