iOS 关于全面屏适配的方案及UI在不同尺寸下适配方案

栏目: IOS · 发布时间: 6年前

内容简介:前言全面屏刚出时,网上有说反人类。但过去这么久了,趋于技术的进步或看久了,大家也都慢慢习惯了(只是笔者还是买不起全面屏)。

前言

全面屏刚出时,网上有说反人类。但过去这么久了,趋于技术的进步或看久了,大家也都慢慢习惯了(只是笔者还是买不起全面屏)。 官方适配中文版文档 也出来了。

iOS 关于全面屏适配的方案及UI在不同尺寸下适配方案

iOS 关于全面屏适配的方案及UI在不同尺寸下适配方案

图源:( https://baijiahao.baidu.com/s?id=1579022360572390261픴=spider&for=pc)

回想起刚开始适配全面屏用了一种暴力、并不优雅的方法,以至于后来出了XS(MAX)和XR后出了bug。所以选择一种可靠的、优雅的方案是很有必要的。如今网上关于探讨适配全面屏的文章五花八门,笔者将探究其中的各种方案。

由于笔者水平有限,眼界狭窄,难免出现疏忽的地方,希望大神提出更好的方案。

全面屏的数据

  • 顶部

iOS 关于全面屏适配的方案及UI在不同尺寸下适配方案

iOS 关于全面屏适配的方案及UI在不同尺寸下适配方案

从以上两图,我们可以看出全面屏的顶部Statusbar变高了,其他部分没变。

Largetitle是iOS11中新加入的特性。当然我们开发中很少用到Largetitle。

  • 底部

全面屏底部多出了高度为34的Home Indicator 区域。

iOS 关于全面屏适配的方案及UI在不同尺寸下适配方案

分辨率(图源:blog.csdn.net/sinat_15735… )

个人总结的全面屏适配观点

1.虽然笔者买不起XStyle,但是虚拟器应该能满足适配的所有测试。

所以开发中,请优先使用全面屏开发。笔者有个朋友,开发时用非全面屏,偶尔会出现忘记适配全面屏问题。如果用全面屏,开发效率将会进一步提升。毕竟界面适配全面屏的时候,很难忘记适配非全面屏。

2.App显示界面大小是由App启动页决定的。

记得iPhoneX刚出时,App在其上面运行显示居中,大小和6s一样,上下各有一块黑块。尝试打印出分辨率惊奇发现不是官方宣传的1124,2436。把启动页大小改了宏才达到预想效果。如果用xib,那就没什么问题。启动页用图片的话,要适配上@3x的图片。

3.宏怎么定义

宏里只要能区分开XStyle,其他高度就好说。

网上很多教程都是按照分辨率来区分。然而,根据上面我们可以发现,XStyle的分辨率并非固定。所以单纯按照分辨率是不行的。笔者一开始适配X就是这样,后来XSMax出了问题,被迫强行更新XCode10用XSMax,发现宏没写好(顺便吐槽一句,XCode10真是噩梦,又懒得下回去)。

也有旧教程是按照屏幕宽高。但根据上面数据也不是固定的,所以要注意。这两种失效宏都列在下面。

// 单纯根据分辨率
#define K_iPhoneXStyle ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1125, 2436), [[UIScreen mainScreen] currentMode].size) : NO)

// 单纯根据屏幕宽高
#define  K_iPhoneXStyle (KScreenWidth == 375.f && KScreenHeight == 812.f ? YES : NO)

其实笔者很想弄出一个通用的宏,不那么怕出新机会失效的宏,但奈何实在想不到。只能照XStyle不一样的数据写出宏。下面列出的宏在XS Max时还是有效的。至于以后就说不准了,各位还是要根据新机分辨率或者宽高适当修改。

#define K_iPhoneXStyle ( (CGSizeEqualToSize(CGSizeMake(414, 896), [[UIScreen mainScreen] bounds].size)) || ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1125, 2436), [[UIScreen mainScreen] currentMode].size) : NO) )

或者

#define  K_iPhoneXStyle ((KScreenWidth == 375.f && KScreenHeight == 812.f ? YES : NO) || (KScreenWidth == 414.f && KScreenHeight == 896.f ? YES : NO))

还有其他的宏

#define KScreenWidth ([UIScreen mainScreen].bounds.size.width)
#define KScreenHeight ([UIScreen mainScreen].bounds.size.height)
#define K_iPhoneXStyle ((KScreenWidth == 375.f && KScreenHeight == 812.f ? YES : NO) || (KScreenWidth == 414.f && KScreenHeight == 896.f ? YES : NO))
#define KStatusBarAndNavigationBarHeight (K_iPhoneXStyle ? 88.f : 64.f)
#define KStatusBarHeight (K_iPhoneXStyle ? 44.f : 20.f)
#define KTabbarHeight (K_iPhoneXStyle ? 83.f : 49.f)
#define KMagrinBottom (K_iPhoneXStyle ? 34.f : 0.f)

还有些宏,是适配字体或者view用的。将在后面介绍UI在不同尺寸下适配方案再提起。

#define KScaleWidth(width) ((width)*(KScreenWidth/375.f))
#define IsIphone6P          SCREEN_WIDTH==414
#define SizeScale           (IsIphone6P ? 1.5 : 1)
#define kFontSize(value)    value*SizeScale
#define kFont(value)        [UIFont systemFontOfSize:value*SizeScale]

最后说一句,利用宏来写虽然简单,但有以下弊端。

  • 找不到能可靠性强的宏XStyle。毕竟以后的手机分辨率或者屏幕宽高肯定会变化(即使短时间内不会)。要适应新机型就要重新上线。

  • 假如以后新出的机刘海长度变了,到时又要修改宏。

  • App适配起横屏,也挺麻烦的。

即使有弊端,笔者还是觉得这么写可行。毕竟大多数App都不用支持横屏,而且屏幕短时间内不会有太大变动。

人要保持一颗活到老学到老的心,这些弊端有方法避免。

iOS11出了安全区域SafeArea这个概念,用得好可以解决以上问题。要点时间适应。

弊端是目前大多App都支持iOS11.0-,这样就要写判断版本号,代码多将近一倍。

优点也很明显

  • 如果苹果新出了新机型,不用改动代码适应的可能性非常大。这意味着不用为了适配问题上线新版本。

  • 横屏时顶部不会有偏移。有横屏需求的话,也许就不用为了横屏做额外适配。

等以后App iOS11.0起步的时候(短时间不太可能qaq),个人感觉SafeArea将会成为主流。

但至少目前,看上去很美好,实际上适配写多一倍的代码让笔者望而生畏。

4.SafeAreaiOS7以后,苹果给UIViewController引入了topLayoutGuide 和 bottomLayoutGuide两个属性。用于表示顶部或底部的高度。根据有无显示状态栏、导航栏、tabBar返回高度。如果VC内嵌VC,内嵌的VC将视为另起的顶底坐标体系,不受原状态栏等影响。你可能听都没听过这两属性,因为开发中我们习惯直接用宏写上数值,几乎不用这两个属性。更何况那时iPhone还没有全面屏这种东西。

到了iOS11,苹果弃用了topLayoutGuide和bottomLayoutGuide两个属性。引入了safeArea代替。官方的建议是  不能被遮挡的内容和控件在安全区域范围内显示。如果视图底部有按钮,在全面屏下,请约束底部距离34,不要影响到Home功能。

  • safeAreaLayoutGuide

此属性适用于自动布局。

iOS 关于全面屏适配的方案及UI在不同尺寸下适配方案

使用前

[NSLayoutConstraint constraintWithItem:someView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:0];

使用后

[NSLayoutConstraint constraintWithItem:someView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view.safeAreaLayoutGuide attribute:NSLayoutAttributeTop multiplier:1.0 constant:0];

笔者用的是Masonry。注意该属性是iOS11后出现的。因为X发布时,最低版本超过了11,所以全面屏都能用此属性。在这里可以看出,因为11不支持,这代码多了一倍。我们完全可以不用这新属性,减少一半代码爽歪歪。所以笔者目前开发还未使用。

    [testView mas_makeConstraints:^(MASConstraintMaker *make) {

        make.height.equalTo(@44);
        if (@available(iOS 11.0,*)) {
            make.top.equalTo(self.view.mas_safeAreaLayoutGuideTop);
            make.left.equalTo(self.view.mas_safeAreaLayoutGuideLeft);
            make.right.equalTo(self.view.mas_safeAreaLayoutGuideRight);
        } else {
            make.top.equalTo(self.view).offset(KStatusBarHeight);
            make.left.equalTo(self.view);
            make.right.equalTo(self.view);
        }
    }];

笔者没有全面屏,只能展示一下热点了。该效果与用宏KStatusBarHeight一样。

(注:此手机系统12.0)

iOS 关于全面屏适配的方案及UI在不同尺寸下适配方案

但横屏时就不一样了。宏写法会与上方有一段KStatusBarHeight距离,此方法没有。这就是笔者说的其中一个优点,然而并没有好到足够让笔者用它的程度。

iOS 关于全面屏适配的方案及UI在不同尺寸下适配方案

最后有两个观点。

  1. 对于在ViewController的view,推荐使用mas_safeAreaLayoutGuide。这样就能动态更改,即使横屏。

  2. 对于在View之间的约束,推荐使用mas_left。一来没必要用safeArea,二来不用判断版本号,减少代码量。

  • safeAreaInsets

此属性适用于手动计算frame。

X竖屏时控制器view的safeAreaInsets是(44,0,34,0);横屏(0, 44, 21, 44)。

iOS 关于全面屏适配的方案及UI在不同尺寸下适配方案

用到的是这个方法- (void)viewSafeAreaInsetsDidChange;

- (void)viewDidLoad {
    [super viewDidLoad];

    self.view.backgroundColor = [UIColor redColor];
    UIView *testView = [[UIView alloc] initWithFrame:CGRectMake(0, KStatusBarHeight, KScreenWidth, 200)];
    testView.backgroundColor = [UIColor blackColor];
    [self.view addSubview:testView];
    self.testView = testView;
}

- (void)viewSafeAreaInsetsDidChange {
    [super viewSafeAreaInsetsDidChange];
    NSLog(@"%s", __func__);

    [self updateFrame];
}

- (void)updateFrame {
    if (@available(iOS 11.0, *)) {
        CGRect newFrame = self.testView.frame;
        newFrame.origin.x = self.view.safeAreaInsets.left;
        newFrame.size.width = KScreenWidth - self.view.safeAreaInsets.left - self.view.safeAreaInsets.right;
        self.testView.frame = newFrame;
    }
}

iOS 关于全面屏适配的方案及UI在不同尺寸下适配方案

iOS 关于全面屏适配的方案及UI在不同尺寸下适配方案

用frame的话,不仅低于11的系统,就连高于11的系统,要适配起横屏问题都比较麻烦。

所以笔者是趋向于用约束的,见仁见智吧。

这里再贴出两篇说SafeArea的文章。

最近很火的 Safe Area 到底是什么

笔者很久不用xib了,贴出现成的一篇文章。

iOS XIB使用Safe Area后在iOS9和10上面出现的问题和解决方案

4.automaticallyAdjustsScrollViewInsets 和 contentInsetAdjustmentBehavior

  • automaticallyAdjustsScrollViewInsets:

在iOS7.0以后,相对于ScrollView新增属性,默认为YES,系统会根据所在界面的astatus bar, search bar, navigation bar, toolbar, or tab bar等自动调整ScrollView的inset。

- (void)viewDidLoad {
   [super viewDidLoad];
   self.title = @"我是导航条";

   self.view.backgroundColor = [UIColor redColor];
   UITableView *testTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, KScreenWidth, KScreenHeight) style:UITableViewStylePlain];
   testTableView.backgroundColor = [UIColor blueColor];
   testTableView.delegate = self;
   testTableView.dataSource = self;
   [self.view addSubview:testTableView];

}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
   UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
   if (!cell) {
       cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
   }
   cell.textLabel.text = [NSString stringWithFormat:@"%ld", (long)indexPath.row];
   return cell;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
   return 20;
}

iOS 关于全面屏适配的方案及UI在不同尺寸下适配方案

iOS 关于全面屏适配的方案及UI在不同尺寸下适配方案

可以看到没有改变tableView的frame,只是显示范围变了。

如果没有这个属性,我们要实现同样的效果,tableView尺寸要这样设置。当然手动修改insets也是可以的。

    x = 0, 
    y = KStatusBarAndNavigationBarHeight, 
    width = KScreenWidth, 
    height = KScreenHeight - KStatusBarAndNavigationBarHeight

但是要注意:这种自动调整是在ScrollView是其根视图添加的的第一个控件的时候,才会出现自动调整的效果。详情查看automaticallyAdjustsScrollViewInsets属性

  • contentInsetAdjustmentBehavior

iOS11中废弃了automaticallyAdjustsScrollViewInsets,取而代之的是contentInsetAdjustmentBehavior属性。

该属性原理和automaticallyAdjustsScrollViewInsets原理相似,是为了进一步适应安全区域。

iOS 关于全面屏适配的方案及UI在不同尺寸下适配方案

如果你需要自定义内边距,代码将变成以下这样。

if (@available(iOS 11.0, *)) {
        self.tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
} else {
        self.automaticallyAdjustsScrollViewInsets = NO;
}

contentInsetAdjustmentBehavior各个值之间的区别

链接里有这观点

iOS 关于全面屏适配的方案及UI在不同尺寸下适配方案

最后来谈一下关于全面屏的适配方案。

  • 支持横屏的App,每个界面带ScrollView的吧。

  • 如果你的App最低支持iOS11.0,那么用safeAreaLayoutGuide约束将非常刺激。横屏基本不用管,除非排版需求不一样。

  • 如果你的App要适配11.0-,不支持横屏。那么采用宏来写目前是主流。只是以后出了新类型可能要稍微修改一下宏,并且运行逐个界面检查。目前11.0起步的App毕竟少数,用safeAreaLayoutGuide反而要多判断版本号。看以后App支持版本号趋势吧。

  • 如果你的App要适配11.0-,支持横屏。建议用约束。用frame可能写死人,自求多福吧。

  • 横屏有时实在没办法解决某些问题的话,就写两套。 关于iOS横竖屏适配

电话、热点状态栏问题

有电话打进来、手机开了热点有连接,状态栏会变长20。虽然很多App并未对这些情况适配,但优秀的App应该要处理好。

iOS 关于全面屏适配的方案及UI在不同尺寸下适配方案

横屏和电话热点并无直接关系。横屏状态下默认状态栏是不显示的。

当状态栏增高时,App的控制器的view将会下移20,但是高度却不变。tabBar不会有任何改变。所以如果某个界面scrollView一直到底的话,最好用约束到底部,这样调用viewWillLayoutSubviews时就会修改scrollView高度。建议不要写高度,不然会出现scrollView底部显示丢失20高度的问题。

个人总结的UI在不同尺寸下适配方案

这时就要说回上面提到的宏了。

#define KScaleWidth(width) ((width)*(KScreenWidth/375.f))
#define IsIphone6P          SCREEN_WIDTH==414
#define SizeScale           (IsIphone6P ? 1.5 : 1)
#define kFontSize(value)    value*SizeScale
#define kFont(value)        [UIFont systemFontOfSize:value*SizeScale]

1.UILabel的适配问题

相信开发过程中都遇到过字体适配问题。

UIFont

App如果要设置全局字体,可以通过Swizzing修改。或者像以上宏一样,传进参数,修改字体大小。

看过一篇文章,淘宝在Plus机型的字体都加大成1.5倍。笔者买不起Plus机型,更不敢装淘宝这种剁手App,所以无法验证。

iOS字体大小适配的几种方法

本文要探究的是UILabel显示的问题。重点并不在UIFont上。

先研究UILabel。

  • UILabel默认字体为[UIFont systemFontOfSize:17];// 每个中文文字宽度为17。但字母、数字宽度并非固定。例如1比0瘦。注意这将可能是个坑。

  • numberOfLines只有设定了宽度约束的情况下起效。否则Label只会显示一行。

  • UILabel默认垂直方向会居中显示,即当Label高度高于text高度,文字在垂直方向上居中。水平方向上textAlignment可以设置。

iOS 关于全面屏适配的方案及UI在不同尺寸下适配方案

如果要设置成顶部对齐,有好几种方法。最粗暴的是用UITextView。

iOS 关于全面屏适配的方案及UI在不同尺寸下适配方案

  • lineBreakMode设置文字过长时省略号放哪。

label.lineBreakMode = NSLineBreakByCharWrapping;以字符为显示单位显

示,后面部分省略不显示。

label.lineBreakMode = NSLineBreakByClipping;剪切与文本宽度相同的内

容长度,后半部分被删除。

label.lineBreakMode = NSLineBreakByTruncatingHead;前面部分文字

以……方式省略,显示尾部文字内容。

label.lineBreakMode = NSLineBreakByTruncatingMiddle;中间的内容

以……方式省略,显示头尾的文字内容。

label.lineBreakMode = NSLineBreakByTruncatingTail;结尾部分的内容

以……方式省略,显示头的文字内容。

label.lineBreakMode = NSLineBreakByWordWrapping;以单词为显示单位显

示,后面部分省略不显示。
  • adjustsFontSizeToFitWidth //设置字体大小适应label宽度

  • minimumScaleFactor

iOS 关于全面屏适配的方案及UI在不同尺寸下适配方案

  • sizeToFit

改变Label的尺寸以显示文字。需要注意的是,需要在label.text赋值后执行。如果宽高都进行了约束,那么调用sizeToFit将无效果。如果只约束了宽度,并且行数非1,那么sizeToFit会修改Label的高度;如果只约束了高度,或者行数为1,那么sizeToFit只会修改Label的宽度。如果二者皆未约束,只会修改Label宽度。

  • sizeToFit和adjustsFontSizeToFitWidth的区别。

从字面上我们就能区分开,前者是改变Label的宽高,后者是改变字体大小。

反正以后做项目的时候,明确需求,我们是固定了字体的大小来适配label的宽,还是固定了label的宽来适配字体的大小,前者用sizeToFit,后者用adjustFontsToFit。

以淘宝举例。为了研究只能下这剁手App了。

se下搜iPhoneX

iOS 关于全面屏适配的方案及UI在不同尺寸下适配方案

6s下搜iPhoneX

iOS 关于全面屏适配的方案及UI在不同尺寸下适配方案

这里无视Label左边的图标(天猫、双11)。

其商品标题有两行。之前提到了,如果不作处理,Label默认垂直方向居中。如果文字长度只有一行,那会显示奇怪。所以笔者来约束的话,先做垂直方向处理,并且约束了宽度距离左边图片,距离cell.contentView右边。然后设置行数为2(或者约束高度,行数为0,lineBreakMode裁剪)。

还有一种方案是Label根据文字长度自动适配高度,并设置最大高度限制。输入框高度就用类似方案。网上搜UITextView自动高度一堆教程。笔者懒得翻就不弄了。基本原理是根据文字大小长度、Label的宽度、文字间距,算出文字高度,然后设置Label高度为 最大限制高度与文字高度 较小者。

再来看价格和付款人数Label。

笔者设计的话,会采用以下形式。

    priceLabel.font = ...
    [priceLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(imageView.mas_right).offset(...);
        make.height.equalTo(...);
        make.top...;
    }];

    numberOfPeopleLabel.font = ...
    [numberOfPeopleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(priceLabel.mas_right).offset(...);
        make.height.equalTo(...);
        make.top...;
    }];

然后在cell设置model时

    priceLabel.text = ...
    [priceLabel sizeToFit];
    numberOfPeopleLabel.text = ...
    [numberOfPeopleLabel sizeToFit];

总结一下,开发中很少会用到adjustsFontSizeToFitWidth,大多数时候都会顶部对齐、换行、裁剪,或设置自动高度。

2.UI在不同尺寸的适配

来说最后一个非常有用的宏。

#define KScaleWidth(width) ((width)*(KScreenWidth/375.f))

笔者遇到的设计师给的图都是i6屏幕,其宽度为375.f。如果给的图不是,那么将这个宏数值修改即可。

这个宏有什么用呢?

其实就是一个比例转换的问题。不同屏幕下,某些UI可能大小不一样,这时候采用这个宏将会非常方便。

还是举回上面的两张淘宝图例子。

笔者目测(目测而已),cell是不同高度的。假如6s中商品图的宽度150.f,占屏宽0.4。在se中按照比例,320 * 0.4,为128。

那么我们用这个宏,就能一步到位。KScaleWidth(150),在6s中就为150,在se中为128。

除此之外,间距约束用这个宏也有奇效。

这个宏在collectionView中更显神威。

iOS 关于全面屏适配的方案及UI在不同尺寸下适配方案

设计图算好了两个cell的间距,每个cell的大小,整个collectionView的大小,contentInset。这时我们采用这个宏,在不同屏幕下的适配问题将迎刃而解。

这里为什么不写出KScaleHeight呢?

笔者并不是说不能用,只是view通常是被宽度所限制。你见过微信的cell文本内容高度有变化吗hhhhh。就像图片尺寸变了,高度也是被图片宽度带动,而不是屏幕高度。

当然此宏虽很有用,但是开发中还是要经过考虑哪些地方需要用。

参考

作者:Hsusue

链接:https://juejin.im/post/5bd2a094518825289f7f3d17


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Music Recommendation and Discovery

Music Recommendation and Discovery

Òscar Celma / Springer / 2010-9-7 / USD 49.95

With so much more music available these days, traditional ways of finding music have diminished. Today radio shows are often programmed by large corporations that create playlists drawn from a limited......一起来看看 《Music Recommendation and Discovery》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

URL 编码/解码
URL 编码/解码

URL 编码/解码

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具