本文首发于CSDN:http://blog.csdn.net/madongchunqiu/article/details/47960745。
给心急的同学先说说结论:(因为我也是一个心急的同学)
对于UILabel,设置number of lines相当于设置了一个纵向的constraint;也即意味着,UILabel设置三个constraint就够了
对于UILabel,横向设置一个”<=”的constraint,可以让UILabel自行适配宽度
文章结尾有测试题哦,喜欢挑战的同学请往后看
先来一段情怀。
第一个项目是纯手码的,MRC,且几乎没有使用第三方库。整天乐呵呵的在纸上计算布局的坐标,各种大小都是根据比例实时计算的。缺点显而易见,布局的代码太多,把逻辑部分的代码都挤没了;好处是,我感觉超可控,几乎没有意料外的bug,也不会怕苹果更新版本影响到app的使用。(注:最新的版本使用storyboard进行界面切换,大多数布局还是手码的)
第二个项目过于庞大,我使用了Storyboard+auto layout,ARC,数个4位数Star且维护活跃的第三方库。设计师对UI的控制超精细,auto layout真正让我从界面布局中解放出来了。不过从最开始机械的使用“横向两个constraint+纵向两个constraint”,到现在主要使用“UITableView-FDTemplateLayoutCell”(https://github.com/forkingdog/UITableView-FDTemplateLayoutCell)进行tableviewcell的布局,还是有些值得讨论和学习的地方。
以下均假设场景为使用UITableViewCell进行布局,UITableViewCell内有数个Label/Image/Button等复杂排版。一般而言,Image和Button的大小不变或者和Cell保持比例关系,比较容易处理;而Label则可能由于文字的长短,字体大小的变化,导致宽度和高度上的变化,带来计算上的麻烦。
(以下两个方法各有运用场景)
采用“横向两个constraint+纵向两个constraint”的方法,精确到point的控制每个界面元素的位置和大小。如下图:
上图为Label设置了Top, Left, Righ和Height4个属性,即可完全控制Label的大小。
但,若Label中需要显示的内容可能占用多行,则可以将number of lines设置为0(有时标题最多显示两排时,则可设置为2),然后将Height这个constraint牵出来作为IB Outlet,则可以在程序中随时修改,如下图:
由于文字长短和cell的宽度均会影响Label的布局,从而决定cell的高度,因此heightForCellAtIndexPath:的返回值需要通过计算得到。因此此cell会实现两个函数:
+ (CGFloat)cellHeightWithData:(MDDataType*)data withCellWidth:(CGFloat)cellWidth;
- (void)updateWithData:(MDDataType*)data;
前者class method由heightForCellAtIndexPath:调用,避免实例化cell。其中调用
[label.text boundingRectWithSize:CGSizeMake(cellWidth, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:label.font} context:nil].size.height
来计算label的高度,然后合成整个cell的高度
后者instance method由cellForRowAtIndexPath:调用,实例化cell中的各个控件参数,然后将上面代码的计算结果赋值给constraint的IB Outlet.
优点:计算快捷效率高,控制精准。
缺点:IB中设置的gap,margin等数值需要和cellHeightWithData此函数中使用的hardcode数值保持一致,一个数据两方维护,简直不能忍。另:若Label的字体大小变化,则可能需要重新设置,不方便。
利用constraint中的”ratio”,”multiply”,”>=”,”<=”等设置,完成auto layout的自动布局。
这个设置是在前面的设置中,删掉了Height这个constraint,这个时候,可以理解为number of lines充当了第四个constraint,从而可以让auto layout进行布局。果不其然,auto layout提示说该布局和运行后不符(运行后被auto layout到正确的位置了)。这个warning一方面告诉我们说auto layout可以正确handle这个label,另一方面告诉我们应该用”Update Frame”这个方法将Label摆放正确。
另一种情况,某些label可能无法占满整个横向区域,其后可能会有别的控件,可以设置”<=”constraint,使其进行自动布局,如图:
自适应布局,加上前面提到的“UITableView-FDTemplateLayoutCell”(https://github.com/forkingdog/UITableView-FDTemplateLayoutCell),所有关于布局之类的margin,gap等等,就只用在IB中搞定了。几乎没必要牵出constraint作为outlet来手动设置了。并且cell所需要实现的函数仅为:
- (void)updateWithData:(MDDataType*)data;
在heightForRowAtIndexPath:和cellForRowAtIndexPath:中均调用此函数即可。【具体请参考UITableView-FDTemplateLayoutCell的文档和源代码】
优点:布局全部在IB中,代码中几乎可以不用管
缺点:由于heightForRowAtIndexPath中需要实例化cell,且进行布局计算,因此效率会稍低(当然,会有些优化方法)
(以下标题均描述uitableview中各个cell的布局难度)
hardcode吧,多好啊
(缺图)
比如高度和宽度比例为16:9,使用class method进行计算吧,多好啊。
+ (CGFloat)cellHeightWithData:(MDDataType*)data withCellWidth:(CGFloat)cellWidth;
(缺图)
使用UITableView-FDTemplateLayoutCell吧
(缺图)
需要特殊情况特殊对待
(不缺图)
i) 某个列表由tableview实现,每个cell都有很多信息,其中Title可长可短,短的显示一行,长的最多显示两行。标题旁显示Date,有如下四种情况:
情况1:标题占一行,且可以在右侧放置日期,则标题和日期放置在同一行
|——————————————————|
| Title Short Date |
| (Others) |
情况2:标题占一行,但第一行容纳不下日期,日期放在第二行
|——————————————————|
| Title Ratherrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr Long |
| Date |
| (Others) |
情况3:标题占两行,第二行可以在右侧防止日期,则日期放置在第二行
|——————————————————|
| Title Verrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr |
| rrrrrrrrrrrrrrrry Long Date |
| (Others) |
情况4:标题占两行,但第二行容纳不下日期,则日期放在第二行,标题Trunk Tail,显示”…”
|——————————————————|
| Title Terrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr |
| rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrriblely … Date |
| (Others) |
ii) 更多习题。。。