iOS11 & iPhone X 适配指南

百家 作者:iOS开发 2017-09-22 11:24:22

点击上方“iOS开发”,选择“置顶公众号”

关键时刻,第一时间送达!


苹果WWDC开发者大会上,终于发布了大家期待已久的iOS 11,有些新特性功能确实出人意料。不过大的方面苹果貌似也就 AR 和 GM 机器学习了,9月13日凌晨1点,苹果开了新品发布会,相信大家都已经知道Phone X 的刘海了,看起来不是很雅观,对于iOS开发者来说,适配工作也带来了麻烦,iOS11在新旧API 方面做了新的改动,未来App Store就会出现很多大量的APP更新,针对iOS11和iPhone X的适配。


下面针对已经了解的出现的问题进行适配的说明。



屏幕未充满?


相信一部分开发者已经在着手适配iPhone X 和iOS11 了,xcode9测试版运行自己的项目会发现项目没有充满屏幕,上下会有黑色区域的情况,大家别慌,这是没有设置对应的启动图,iPhone X对应像素 1125 * 2436

大家可以自己添加图片或者准备一张尺寸:1125 * 2436的启动图片, 移动到LaunchImage的Finder目录中, 并在LaunchImage中的Contents.json文件中增加 (注意Json格式):


{

    "extent" : "full-screen",

    "idiom" : "iphone",

    "subtype" : "2436h",

    "filename" : "图片名字.png",

    "minimum-system-version" : "11.0",

    "orientation" : "portrait",

    "scale" : "3x"

}


UITableview UICollectionView MJRefresh下拉刷新错乱


  • iOS11 UIScrollView and UITableView的新特性


如果有一些文本位于UI滚动视图的内部,并包含在导航控制器中,现在一般navigationContollers会传入一个contentInset给其最顶层的viewController的scrollView,在iOS11中进行了一个很大的改变,不再通过scrollView的contentInset属性了,而是新增了一个属性:adjustedContentInset,通过下面两种图的对比,能够表示adjustContentInset表示的区域:



contentInsetAdjustmentBehavior属性有以下几个枚举值


typedef NS_ENUM(NSInteger, UIScrollViewContentInsetAdjustmentBehavior) {  

    UIScrollViewContentInsetAdjustmentAutomatic, 

    UIScrollViewContentInsetAdjustmentScrollableAxes,

    UIScrollViewContentInsetAdjustmentNever,

    UIScrollViewContentInsetAdjustmentAlways,

}


@property(nonatomic) UIScrollViewContentInsetAdjustmentBehavior contentInsetAdjustmentBehavior;

@property(nonatomic, readonly) UIEdgeInsets adjustedContentInset;


//adjustedContentInset值被改变的delegate

- (void)adjustedContentInsetDidChange; 

- (void)scrollViewDidChangeAdjustedContentInset:(UIScrollView *)scrollView;


  • 1 automatic 和scrollableAxes一样,scrollView会自动计算和适应顶部和底部的内边距并且在scrollView 不可滚动时,也会设置内边距.

  • 2 scrollableAxes 自动计算内边距.

  • 3 never不计算内边距

  • 4 always 根据safeAreaInsets 计算内边距

  • 在我们项目中写tableview的时候可以加下面的代码来解决此问题


if (@available(iOS 11.0, *)) {

    _tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;

    _tableView.contentInset = UIEdgeInsetsMake(64, 0, 49, 0);//iPhoneX这里是88

    _tableView.scrollIndicatorInsets = _tableView.contentInset;

}


在我自己项目中,我发现如果用的系统的导航栏的话距离上部可为0反而加上会有问题,列表会下移你设置的数值


_tableView.contentInset = UIEdgeInsetsMake(0, 0, 49, 0);

  // 实际项目中这样也有出现列表的下面可能会被遮挡(针对部分项目,视情况而定) 针对我自己的项目我设置了向下导航栏的高度看似没有问题了


tableView cell高度问题和头视图尾视图会有很大间隙


  • 这个应该是UITableView最大的改变。我们知道在iOS8引入Self-Sizing 之后,我们可以通过实现estimatedRowHeight相关的属性来展示动态的内容,实现了estimatedRowHeight属性后,得到的初始contenSize是个估算值,是通过estimatedRowHeight x cell的个数得到的,并不是最终的contenSize,tableView不会一次性计算所有的cell的高度了,只会计算当前屏幕能够显示的cell个数再加上几个,滑动时,tableView不停地得到新的cell,更新自己的contenSize,在滑到最后的时候,会得到正确的contenSize。


  • Self-Sizing在iOS11下是默认开启的,Headers, footers, and cells都默认开启Self-Sizing,所有estimated 高度默认值从iOS11之前的 0 改变为UITableViewAutomaticDimension:


@property (nonatomic) CGFloat estimatedRowHeight NS_AVAILABLE_IOS(7_0); // default is UITableViewAutomaticDimension, set to 0 to disable


  • 如果目前项目中没有使用estimateRowHeight属性,在iOS11的环境下就要注意了,因为开启Self-Sizing之后,tableView是使用estimateRowHeight属性的,这样就会造成contentSize和contentOffset值的变化,如果是有动画是观察这两个属性的变化进行的,就会造成动画的异常,因为在估算行高机制下,contentSize的值是一点点地变化更新的,所有cell显示完后才是最终的contentSize值。因为不会缓存正确的行高,tableView reloadData的时候,会重新计算contentSize,就有可能会引起contentOffset的变化。iOS11下不想使用Self-Sizing的话,可以

    通过以下方式关闭:


self.tableView.estimatedRowHeight = 0;

self.tableView.estimatedSectionHeaderHeight = 0;

self.tableView.estimatedSectionFooterHeight = 0;


安全区域(Safe Area)


从iOS 7以来,我们在整个操作系统中都有这些半透明的bars,苹果鼓励我们通过这些bars绘制内容,我们是通过viewController 的edgesForExtendedLayout属性来做这些的。

iOS 7 开始,在 UIViewController中引入的 topLayoutGuide 和 bottomLayoutGuide 在 iOS 11 中被废弃了!取而代之的就是safeArea的概念,safeArea是描述你的视图部分不被任何内容遮挡的方法。 它提供两种方式:safeAreaInsets或safeAreaLayoutGuide来提供给你safeArea的参照值,即 insets 或者 layout guide。 safeArea区域如图所示:



iOS11 为UIViewController和UIView增加了一个新的方法 - (void)viewSafeAreaInsetsDidChange;


这个方法如名字一样 在安全区域改变后会被调用, 我们可以在需要适配的UIViewController和UIView中重写这个方法, 并且在这个方法中来根据safeAreaInsets属性更新子视图控件的布局位置.


这里有一点要注意的是当UIViewController调用- (void)viewDidLoad时它的所有子视图的safeAreaInsets属性都等于UIEdgeInsetsZero, 也就是说在- (void)viewSafeAreaInsetsDidChange;方法调用前 是无法通过当前视图控制器的子视图获取到safeAreaInsets的, 不过获取当前window对象的safeAreaInsets属性用来计算也是可以的, 但是不建议这么做, 一个视图控制器的子视图的处理当然要以它所在的控制器为准.


  • (void)viewSafeAreaInsetsDidChange在UIViewController中第一次调用的时间是在- (void)viewWillAppear:(BOOL)animated调用之后, 在- (void)viewWillLayoutSubviews调用之前.


当然如果你要改变一个UIViewController的safeAreaInsets值, 可以通过设置addtionalSafeAreaInsets属性来实现, 例如你要自定义一些特殊的样式时.


如果你改变了一个UIViewController的safeAreaInsets属性值, - (void)viewSafeAreaInsetsDidChange也会被调用.


采用SB或者xib 布局 Autolayout 视图的 top 和 bottom 一般参照的是 Top Layout Guide 和 Bottom Layout Guide. 但是iOS11 废弃了改用安全区域(SafeArea)做参考来做适配







滑动删除会蹦?


在iOS8之后,苹果官方增加了UITableVIew的右滑操作接口,即新增了一个代理方法(tableView: editActionsForRowAtIndexPath:)和一个类(UITableViewRowAction),代理方法返回的是一个数组,我们可以在这个代理方法中定义所需要的操作按钮(删除、置顶等),这些按钮的类就是UITableViewRowAction。这个类只能定义按钮的显示文字、背景色、和按钮事件。并且返回数组的第一个元素在UITableViewCell的最右侧显示,最后一个元素在最左侧显示。从iOS 11开始有了一些改变,首先是可以给这些按钮添加图片了,然后是如果实现了以下两个iOS 11新增的代理方法,将会取代(tableView: editActionsForRowAtIndexPath:)代理方法:


// Swipe actions

// These methods supersede -editActionsForRowAtIndexPath: if implemented

- (nullable UISwipeActionsConfiguration *)tableView:(UITableView *)tableView leadingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath

- (nullable UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath


这两个代理方法返回的是UISwipeActionsConfiguration类型的对象,创建该对象及赋值可看下面的代码片段:


- ( UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath {

    //删除

    UIContextualAction *deleteRowAction = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleDestructive title:@"delete" handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) {

        [self.titleArr removeObjectAtIndex:indexPath.row];

        completionHandler (YES);

    }];

    deleteRowAction.image = [UIImage imageNamed:@"icon_del"];

    deleteRowAction.backgroundColor = [UIColor blueColor];


    UISwipeActionsConfiguration *config = [UISwipeActionsConfiguration configurationWithActions:@[deleteRowAction]];

    return config;

}


创建UIContextualAction对象时,UIContextualActionStyle有两种类型,如果是置顶、已读等按钮就使用UIContextualActionStyleNormal类型,delete操作按钮可使用UIContextualActionStyleDestructive类型,当使用该类型时,如果是右滑操作,一直向右滑动某个cell,会直接执行删除操作,不用再点击删除按钮,这也是一个好玩的更新。


滑动操作这里还有一个需要注意的是,当cell高度较小时,会只显示image,不显示title,当cell高度够大时,会同时显示image和title。我写demo测试的时候,因为每个cell的高度都较小,所以只显示image,然后我增加cell的高度后,就可以同时显示image和title了。见下图对比:



目前暂时遇到的问题解决,还是等20号正式版推出再做详细的适配和更改,毕竟测试版的问题和bug 还是很多的。


码友们粪叉(iPhone X)你们买了么????



  • 作者:zombieEngineer

  • 链接:http://www.jianshu.com/p/ef423efd5650

  • 來源:简书

  • iOS开发整理发布,转载请联系作者授权

【点击成为安卓大神】

关注公众号:拾黑(shiheibook)了解更多

[广告]赞助链接:

四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/

公众号 关注网络尖刀微信公众号
随时掌握互联网精彩
赞助链接