屏幕适配

7 9月

ios屏幕适配主要适配三个方面:Points,Pixels & PPI,SafeArea(留海屏),再加个:横竖屏

  • iPhone尺寸
  • Points
  • Pixels & PPI
  • SafeArea

iPhone尺寸

历年iPhone尺寸点这里
iOS Design Guidelines

Points

和web开发不同,native中尺寸是没有单位的,或者说只有默认的固定单位,iOS就是pt,Android就是dp,所以iOS布局就是对Points进行适配。

项目中UED不可能每款iPhone出一版视觉稿,例如只出iPhone6P的视觉稿,程序员需要根据用户实际机型进行尺寸缩放,即给代码中的尺寸数字乘以一个系数。

iPhone的宽度尺寸有三种:320,375,414。(就像web抛弃低版本IE一样,通常<iPhone6版本的320尺寸的可以放弃,只适配375,414两种尺寸)

// 是否为横屏的宏
#define IS_LANDSCAPE (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation]))

// 根据横屏竖屏取屏幕宽高
#define SCREEN_WIDTH (IS_LANDSCAPE ? [[UIScreen mainScreen] bounds].size.height : [[UIScreen mainScreen] bounds].size.width)
#define SCREEN_HEIGHT (IS_LANDSCAPE ? [[UIScreen mainScreen] bounds].size.width : [[UIScreen mainScreen] bounds].size.height)

// 根据屏幕尺寸进行缩放
static inline NSInteger UIAdapter (float x){
    CGFloat scale = 414 / SCREEN_WIDTH; // 因为视觉稿是 iphone6P 是 414 宽度
    return (NSInteger)x /scale;
}

static inline CGRect UIRectAdapter(x, y, width, height){
    return CGRectMake(UIAdapter(x), UIAdapter(y), UIAdapter(width), UIAdapter(height));
}

// 暴露给外部调用
#define UI(x) UIAdapter(x)
#define UIRect(x,y,width,height) UIRectAdapter(x,y,width,height)

定义好逻辑分辨率(Point)的适配方法后,代码里就不要直接写数字了,改为调用上面的适配方法:

// 改前
self.sourceLabel.frame.size.width + 15,
// 改后
self.sourceLabel.frame.size.width + UI(15),

// 改前
self.commentLabel = [[UILabel alloc] initWithFrame:CGRect(100, 70, 50, 20)];
// 改后
self.commentLabel = [[UILabel alloc] initWithFrame:UIRect(100, 70, 50, 20)];

思路是:

  • 根据 home 键的方向(iphone X 开始是虚拟 home 键)判断当前是横屏 / 竖屏
  • 根据横屏 / 竖屏取屏幕宽高
  • 缩放系数有两种方式:
    • 方式一:UED视觉稿有统一标准的话(例如固定是 iphone6P)比较简单,如上【scale = 414 / SCREEN_WIDTH】即可
    • 方式二:UED视觉稿机型可能会变的话,基准值可以通过参数传入

适配完,业务代码改造成:

// 原始代码,未经尺寸适配
self.sourceLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 15, 270, 50)];
self.sourceLabel.frame.size.width = 300;

// 改造后的代码,经过尺寸适配
self.sourceLabel = [[UILabel alloc] initWithFrame:UIRect(20, 15, 270, 50)];
self.sourceLabel.frame.size.width = UI(300);

Pixels & PPI

Pixel就是在屏幕上的像素点。PPI(pixels-per-inch)即像素密度,屏幕对角线上像素点数 / 对角线长度,即像素密度,越高画面越清晰。

Apple从iPhone4开始推出了Retina视网膜屏,即2x两倍屏,相同单位尺寸下,能展示两倍的像素。从iPhone6P开始推出Retina HD视网膜屏,即3x三倍屏。

教主在iPhone4发布会上:

Point和Pixel的关系:

  • non-retina : 1 point = 1 pixel
  • Retina : 1 point = 2*2 pixels
  • Retina HD: 1 point = 3*3 pixels

Pixel主要影响的是图片适配。UED用Sketch等工具会提供原始图片,@2x,@3x屏幕的图片

适配方案:

方案一:不适配,无脑用@3x的图片。@3x的图片在2x两倍屏上也能正常显示,只不过系统会进行压缩,消耗内存,而且@3x图片体积大浪费硬盘,但又不是你手机,Let it go~。但别无脑用@2x的图片,在3x屏幕上100 * 100的区域内,@2x的图片只能显示在66.6 * 66.6的区域,为了撑满100 * 100的显示区域,系统会将@2x的图片拉伸,会导致图片模糊,清晰度不够。

方案二:根据分辨率来适配

self.rightImageView.image = [UIScreen mainScreen].scale == 2 ? [UIImage imageNamed:@"icon.bundle/add@2x.png"] : [UIImage imageNamed:@"icon.bundle/add@3x.png"];

方案三:用 Assets.xcassets 自动适配图片,将 @2x,@3x 的图片拖到设备的相应方格中即可。

用 Assets.xcassets 的好处是,代码中不需要自己去判断分辨率了,也不用指定加@2x,@3x后缀,代码:

self.rightImageView.image = [UIImage imageNamed:@"add@2x.png"]

SafeArea

iPhoneX 推出全面屏,市面上开始普及留海,美人尖。留海会导致页面顶部区域被遮盖。底部虚拟 home 键的 home indictor 区域会响应上滑手势,虽然不像留海会遮挡页面显示,但有如进度条等滑动操作时会受影响。

适配方案就是预留出足够的顶部高度,避免遮盖,将顶部的 statusBar height:20 -> 44

另外别忘了适配横屏,横屏时页面左右两侧同样可能被留海遮盖,底部视频进度条同样可能被虚拟 home 键的 home indictor 区域遮盖,也需要留出来。

所以iPhoneX后的SafeArea如下。竖屏上右下左:{44 0 34 0},横屏上右下左:{0 44 21 44}

当然SafeArea是个指导概念,不是强制的,如果不影响显示和使用,不留白也行。或者像横屏视频进度条,只需要Y轴位置提高一点即可。

@implementation Screen
+ (CGSize)sizeFor58Inch {   // iphonex
    return CGSizeMake(375, 812);
}

+ (CGSize)sizeFor61Inch {   // iphone xr
    return CGSizeMake(414, 896);
}

+ (CGSize)sizeFor65Inch {   // iphone xs max
    return CGSizeMake(414, 896);
}
@end
// 是否是 iphoneX / XR / XMAX
#define IS_IPHONE_X (SCREEN_WIDTH == [Screen sizeFor58Inch].width && SCREEN_HEIGHT == [Screen sizeFor58Inch].height)
#define IS_IPHONE_XR (SCREEN_WIDTH == [Screen sizeFor61Inch].width && SCREEN_HEIGHT == [Screen sizeFor61Inch].height && [UIScreen mainScreen].scale == 2)
#define IS_IPHONE_XMAX (SCREEN_WIDTH == [Screen sizeFor65Inch].width && SCREEN_HEIGHT == [Screen sizeFor65Inch].height && [UIScreen mainScreen].scale == 3)

// 这三种机型有留海屏
#define IS_IPHONE_X_XR_MAX (IS_IPHONE_X || IS_IPHONE_XR || IS_IPHONE_XMAX)

// 如果是留海屏,调整 statusBar 高度
#define STATUSBAR_HEIGHT (IS_IPHONE_X_XR_MAX ? 44 : 20)

在IOS11中,UIScrollView,UICollectionView,UITableView已经自动适配了留海屏。

PS:对于AutoLayout的布局,系统同样提供了UILayoutGuide坐标体系。

PS:ReactNative里有SafeAreaView可以直接用

发表评论

电子邮件地址不会被公开。 必填项已用*标注