屏幕适配总结
曾经,iOS开发是不需要考虑屏幕适配问题的,因为只有一种屏幕尺寸。而现在已经有了4种屏幕,4,5,6,6P,因此屏幕适配也成了iOS开发中必须考虑的问题。并且,这4种屏幕的宽高比全部都不一样,所以简单的按比例缩放并不能解决问题。我们最近做的一个APP也处理了屏幕适配,本文总结一下这方面的心得
根据屏幕类型判断
我们的做法是根据设备类型,写一些if…else,或者switch语句
判断机型可以使用screen的height(不能使用width,因为4和5的width是一样的,都是320),也可以使用API里的宏,都差不多
我个人感觉,if…else似乎是不可避免的,虽然有auto layout,但是有一些大的布局改动,或者字体大小,不用判断似乎是无法解决的
比如说,为了达到最佳显示效果,我们在大的屏幕上使用CollectionView,而在4S上使用TableView,用自动布局应该是没有办法做到的。或者根据屏幕的大小,切换字体大小,好像也只能通过if…else来实现
根据屏幕类型适配,代码类似:
1 | if(screenType == LosScreenType6P){ |
frame计算
我们也用了比较多的“硬计算”,比如对于UICollectionView里的每个cell的width,我们是这么处理的:
1 | CGRect rect = [[UIScreen mainScreen] bounds]; |
1 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath |
我们规定CollectionView里每行有3个单元格,整个section的左右间距是10,列间距是5,因此计算出 (width - 30) / 3就是每个单元格的宽度,单元格的高度也是经过计算写死的
对于这个页面,这种方法很有效。类似这种基于屏幕尺寸做计算的方法,APP里在几个页面都有用到
Masonry
Masonry是我们实现屏幕适配的重要手段之一,本质上是界面约束的语法糖。基本上,我们的做法是:大的页面关系,用计算完成;每个小块里面的相对位置关系,用Masonry来做。在有些场景下,Masonry有非常大的优势。比如说:
设置某个View的宽高比
1 | [thumbImageView mas_makeConstraints:^(MASConstraintMaker *make) { |
此View的宽度与父View同宽,高度是宽度的0.8
设置居中,设置相对边距
1 | [authorName mas_makeConstraints:^(MASConstraintMaker *make) { |
垂直方向与另一个View对齐,左边距离上一个元素的右边5,右边距离父View右边5
类似这种布局,用frame来写会复杂很多,如果再考虑屏幕适配,需要非常多代码。这类的需求,Masonry堪称神器
不过使用中发现,用Masonry布局的View,我们通常会init,或者initWithFrame:CGRectZero。这个View直到经过Masonry处理以后,它的origin和size才能确定,如果在此之前就用到它的origin和size,就会有问题
整体替换UIView
对于适配后变化不大的页面,把if…else写在UIView里,但是有个别页面,完全要根据设备显示不同的View。这种情况比较适合在Controller里做判断,然后load不同的View
设计图
一般会先由美工做出界面的标注图,然后开发再去实现。所以从设计图这个环节,就需要开始考虑界面适配的问题
出几张图
如果以iPhone6为基准出设计图的话,一般很难完美地适配到iPhone4,5和6P上,因为屏幕尺寸差异很大。一般在6上摆得很紧凑好看,在4和5上就会摆不下(溢出屏幕),在6P上则会有比较大的空隙
通常有几种办法:
设计元素的位置和大小,不用具体的数值,而是使用百分比,这样在每种屏幕上会适配。但是通常这只是理想情况,因为4种屏幕的宽高比都不一样,必然会出现拉伸的情况;可能在4上的显示区域和点击区域都会非常小;而且也难以达到最佳视觉效果
尽量考虑流式布局,比如瀑布流,滚动页面等,这样由于页面可滚动,通常不会出现太大的问题。相比之下,把显示区域限制在一个屏幕里,就容易出问题
最完美的办法,出4张设计图,尺寸用具体数值而不是百分比。这种做法可以在每个屏幕上达到最佳效果。当然设计和开发的工作量也是最大的
点和分辨率的关系
iOS在开发的时候,用的单位是point,可是一般只有开发人员才理解这个概念,设计师画图时一般都是用分辨率。在4,5,6上都是2X分辨率,而在6P上是3X分辨率。在设计的时候,必须考虑这点
比如说,字体大小在2x屏上是24px,一定要尽量在3x上设计成36px,这样开发不需要做任何判断,字体大小自然会乘以1.5。一个反面的例子,设计图上2x屏是24px,3x屏是28px,这通常就是一个错误的设计。开发只能根据屏幕类型,设置UIFont的大小,而且28还无法被3整除
再比如说,设计一个元素距离左边距在2x屏上是10px,那么在3x屏上就设计成15px,开发也不需要做任何改动
WEB页面单独出图
还有一种情况,某些页面需要在APP上实现,同时也要在web页面上实现。我们一开始错误的做法,是要求web页面去“精确”地实现APP的设计图。实际上这是不可能的,因为2点:
1、WEB页面使用的CSS布局方法,跟iOS界面开发的思路有很大差异,在实现上会有问题
2、WEB页面要面对的手机屏幕尺寸更多,无论是设计还是开发,要精确地匹配N种屏幕,根本不可能
所以,正确的做法,是针对WEB页面再出一张设计图,其中的尺寸用百分比决定:
如上图,在APP设计图里可以精确确定每个单元格的宽度,而在WEB里,应该指定每个单元格占屏幕宽度的50%。甚至,在WEB上应该用响应式布局来处理尺寸差异巨大的屏幕,不过这是另一个话题,不延伸了
UIFont到底有多大
这个是我们试错试出来的,其实非常简单,设置
1 | [UIFont systemFontOfSize:16] |
这个字体在2x屏上就是32px,在3x屏上会自然变成48px,非常方便
同理,设置一个UIView的frame为
1 | CGRectMake(0, 0, 100, 100); |
在2x屏上是200px * 200px的矩形,在3x屏上是300px * 300px的矩形