胡东东博客

iOS屏幕适配和设计流程分析


iOS开发中,屏幕适配很多使用masonary这个库,但是使用过程中需要知道这些在屏幕适配的过程中与图片相关的工作。

一、基本概念

首先,理解几个概念: Points, Rendered Pixels, Physical Pixels, Physical Device。

渲染像素Rendered Pixels是理论值,真正的物理像素Physical Pixels会小于或等于这个理论值

那么,iOS 开发中,上述单位会有什么对应关系呢?列表回答:

机型屏幕宽高(point)渲染像素(pixel)物理像素(pixel)屏幕对角线长度(英寸)屏幕模式
iPhone 2G, 3G, 3GS320 * 480320 * 480320 * 4803.5(163PPI)1x
iPhone 4, 4s320 * 480640 * 960640 * 9603.5 (326PPI)2x
iPhone 5, 5s320 * 568640 * 1136640 * 11364 (326PPI)2x
iPhone 6, 6s, 7375 * 667750 * 1334750 * 13344.7 (326PPI)2x
iPhone 6 Plus, 6s Plus, 7 Plus414 * 7361242 * 22081080 * 19205.5 (401PPI)3x

所以代码中输出屏幕分辨率大小的时候,其实就是输出的是点

NSLog(@"%f,%f,%f",[UIScreen mainScreen].bounds.size.width,[UIScreen mainScreen].bounds.size.height,[UIScreen mainScreen].scale);

//在6s的模拟器中输出的值
375.000000,667.000000,2.000000

关于上述关系 PaintCode 绘制图形进行了详细说明,可以看The Ultimate Guide To iPhone Resolutions

由上可以看出,所谓的屏幕模式,描述的就是屏幕中一个点有多少个 Rendered Pixels 渲染,对于2倍屏(又称 Retina 显示屏),会有 2 2 = 4 个像素的面积渲染,对于3倍屏(又称 Retina HD 显示屏),会有 3 3 = 9 个像素的面积渲染。

关于 Points 和 Pixels 的描述,参考官方文档: Points Versus Pixels.

iOS 开发中,所有控件的坐标以及控件大小都是以点为单位的,假如我在屏幕上需要展示一张 20 20 (单位:point)大小的图片,那么设计师应该怎么给我图呢?这里就会用到屏幕模式的概念,如果屏幕是 2x,那么就需要提供 40 40 (单位: pixel)大小的图片,如果屏幕是 3x,那么就提供 60 * 60 大小的图片,且图片的命名需要遵守以下规范:

Standard:
<ImageName><device_modifier>.<filename_extension>

High resolution:
<ImageName>@2x<device_modifier>.<filename_extension>

High HD resolution:
<ImageName>@3x<device_modifier>.<filename_extension>

例如:

2x屏幕的设备会自动加载 [email protected] 命名的图片资源,3x屏幕的设备会自动加载 [email protected] 的图片, 现在基本没有 1x屏幕的设备了,可以不用提供这个分辨率的图片了。

二、设计和开发之间的多屏适配问题

现在APP设计开发必须考虑适配大、中、小三种屏幕。所以如何做到交付一套设计稿解决适配大中小三屏的问题?设计和开发之间采用什么协作模式?

一个基本思路是:

  1. 选择一种尺寸作为设计和开发基准;
  2. 定义一套适配规则,自动适配剩下两种尺寸;
  3. 特殊适配效果给出设计效果。

其中看到手机淘宝的设计方案,和我的想法差不多,今天才看到,以前走了弯路。

第一步,视觉设计阶段,设计师按宽度750px(iPhone 6)做设计稿,除图片外所有设计元素用矢量路径来做。设计定稿后在750px的设计稿上做标注,输出标注图。同时等比放大1.5倍生成宽度1125px的设计稿,在1125px的稿子里切图。

第二步,输出两个交付物给开发工程师:一个是程序用到的@3x切图资源,另一个是宽度750px的设计标注图。

第三步,开发工程师拿到750px标注图和@3x切图资源,完成iPhone 6(375pt)的界面开发。此阶段不能用固定宽度的方式开发界面,得用自动布局(auto layout),方便后续适配到其它尺寸。

第四步,适配调试阶段,基于iPhone 6的界面效果,分别向上向下调试iPhone 6 plus(414pt)和iPhone 5S及以下(320pt)的界面效果。由此完成大中小三屏适配。

2.1、为什么选择iPhone 6作为基准尺寸?

当面对大中小三种屏幕需要适配的时候,很容易想到先做好一种屏幕,再去适配剩下两种屏幕。第一个决定是到底以哪种屏幕作为设计和开发的基准尺寸。

我们选择中间尺寸的iPhone 6(750px/375pt)作为基准,基于几个原因:

1、从中间尺寸向上和向下适配的时候界面调整的幅度最小。375pt下的设计效果适配到414pt和320pt偏差不会太大。假设以414pt为基准做出很优雅的设计,到320pt可能元素之间比例就不是那么回事了,比如图片和文字之间视觉比例可能失调。

2、iPhone 6 plus有两种显示模式,标准模式分辨率为1242x2208,放大模式分辨率为1125x2001(即iPhone 6的1.5倍)。可见官方系统里iPhone 6和iPhone 6 plus分辨率之间就存在1.5倍的倍率关系。很多情况下这两种尺寸可以用1.5倍直接等比适配。

3、1242x2208这个奇葩的数值是苹果官方都不愿意公开宣传的一个分辨率,不便于记忆和计算栅格。640x1136虽然是广泛应用的一个分辨率,但是大屏时代依然以小尺寸为设计基准显然不合时宜,设计师会停留在小屏的视角做设计。

所以,iPhone6的750x1334是最适合基准尺寸。只交付一套设计稿,默认用什么规则来适配?前文提到适配策略是先选择iPhone 6作为基准设计尺寸,然后通过一套适配规则自动适配到另外两种尺寸。

这套适配规则总结起来就一句话:文字流式,控件弹性,图片等比缩放。

控件弹性指的是,navigation、cell、bar等适配过程中垂直方向上高度不变;水平方向宽度变化时,通过调整元素间距或元素右对齐的方式实现自适应。这样屏幕越大,在垂直方向上可以显示更多内容,发挥大屏幕的优势。

按照上述默认适配规则,大中小三种屏幕显示效果均相同。有时候想在大屏幕显示更多内容,需要设计出特殊适配效果。比如App store首页焦点图,从iPhone 6适配到iPhone 6 plus时焦点图尺寸和排版做了特殊处理。底下应用列表也从一排3+个变成一排4+个,真正实现了大屏幕显示更多内容的理念。这些就需要设计师给出相应设计稿。

三、布局处理

在使用masonary自动布局时,可以根据6s的屏幕设计,设置一个比例系数,比如

//以6/6s为准宽度缩小系数
#define kJLXWidthScale   [UIScreen mainScreen].bounds.size.width/375.0  

//高度缩小系数
#define kJLXHeightScale  [UIScreen mainScreen].bounds.size.height/667.0 

这样在布局的的时候,可以考虑使用上这个系数设置高度

UIButton *createrButton = [[UIButton alloc] init];
[self.view addSubview:createrButton];
    
UIEdgeInsets padding = UIEdgeInsetsMake(0, 10, 65, 10);
[createrButton setBackgroundImage:[UIImage imageNamed:@"common_button_pink"] forState:UIControlStateNormal];
[createrButton mas_makeConstraints:^(MASConstraintMaker *make){        
    make.left.equalTo(self.view.mas_left).with.offset(padding.left);
   make.height.equalTo(@(60*kJLXHeightScale));
   make.bottom.equalTo(self.view.mas_bottom).with.offset(-padding.bottom);
   make.right.equalTo(self.view.mas_right).with.offset(-padding.right);
}];

这样在5s小屏手机上面,按钮的高度就会根据比例系数来动态调整大小。

四、图片处理

在游戏中,经常会碰到九宫格的可拉伸图片,在ios开发中也可以使用,比如截取一部分拉伸,其他不变

UIImage *img = [UIImage imageNamed:@"popup"];
img = [img resizableImageWithCapInsets:UIEdgeInsetsMake(0, 13, 0, 55) resizingMode:UIImageResizingModeStretch];
self.resizableImgView.image = img;

如果是平铺

可这么实现

UIImage *img = [UIImage imageNamed:@"about"];
img = [img resizableImageWithCapInsets:UIEdgeInsetsMake(0, 11.5, 0, 11) resizingMode:UIImageResizingModeTile];
self.resizableImgView.image = img;

五、状态栏和导航栏的大小

状态栏和导航栏可以通过代码来获取

// 状态栏(statusbar)  
CGRect rectStatus = [[UIApplication sharedApplication] statusBarFrame];  
NSLog(@"status width - %f", rectStatus.size.width); // 宽度  
NSLog(@"status height - %f", rectStatus.size.height);   // 高度  
      
// 导航栏(navigationbar),这个需要你的controller加到了navigationController上面,并且navigationBar不能设置为隐藏,否则获取到的宽高是0
CGRect rectNav = self.navigationController.navigationBar.frame;  
NSLog(@"nav width - %f", rectNav.size.width); // 宽度  
NSLog(@"nav height - %f", rectNav.size.height);   // 高度  

打印结果

status width - 320.000000  
status height - 20.000000  
nav width - 320.000000  
nav height - 44.000000  

六、参考文章

当前页面是本站的「Baidu MIP」版。查看和发表评论请点击:完整版 »