有关iOS11的适配问题

1. 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"
}

2. UITableview UICollectionView MJRefresh下拉刷新错乱

  • iOS11 UIScrollView and UITableView的新特性导致

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

adjustedContentInset.png
adjustedContentInset.png

contentInsetAdjustmentBehavior属性有以下几个枚举值:

/*
1. automatic 和scrollableAxes一样,scrollView会自动计算和适应顶部和底部的内边距并且在scrollView 不可滚动时,也会设置内边距.
2. scrollableAxes 自动计算内边距.
3. never不计算内边距
4. always 根据safeAreaInsets 计算内边距
*/
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;

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

3. tableView 头部视图和尾部视图出现一块留白问题

  • iOS11下tableview默认开启了self-Sizing,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

有两种办法去掉留白:

  • tableView的style:UITableViewStyleGrouped类型,默认tableView开头和结尾是有间距的,不需要这个间距的话,可以通过实现heightForHeaderInSection方法(返回一个较小值:0.1)和viewForHeaderInSection(返回一个view)来去除头部的留白,底部同理。

  • iOS 11上发生tableView顶部有留白,原因是代码中只实现了heightForHeaderInSection方法,而没有实现viewForHeaderInSection方法。iOS 11之后应该是由于开启了估算行高机制引起了bug。添加上viewForHeaderInSection方法后,问题就解决了。或者添加以下代码关闭估算行高,问题也得到解决。

self.tableView.estimatedRowHeight = 0;
self.tableView.estimatedSectionHeaderHeight = 0;
self.tableView.estimatedSectionFooterHeight = 0;

4. 安全区域(Safe Area)

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

5. 滑动删除会崩
  • 在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了。见下图对比:

0 (1).png
0 (1).png

6. navigation bar

  • 导航栏新增了一种大标题样式,默认设置是不开启,所以不需要修改。

  • titleView支持autolayout,这要求titleView必须是能够自撑开的或实现了- intrinsicContentSize

  • 解决办法比较简单,这个搜索框对应的view实现- intrinsicContentSize方法

- (CGSize)intrinsicContentSize {
    return UILayoutFittingExpandedSize;
}

7. 导航栏返回按钮

之前的代码通过下面的方式自定义返回按钮

 UIImage *backButtonImage = [[UIImage imageNamed:@"icon_tabbar_back"]
    resizableImageWithCapInsets:UIEdgeInsetsMake(0, 18, 0, 0)];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:backButtonImage
                                                  forState:UIControlStateNormal
                                               barMetrics:UIBarMetricsDefault];
[[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(0, -60)
                                                    forBarMetrics:UIBarMetricsDefault];

iOS 11 中setBackButtonTitlePositionAdjustment:UIOffsetMake没法把按钮移出navigation bar,
解决方法是设置navigationController的backIndicatorImage和backIndicatorTransitionMaskImage

UIImage *backButtonImage = [[UIImage imageNamed:@"icon_tabbar_back"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
self.navigationBar.backIndicatorImage = backButtonImage;
self.navigationBar.backIndicatorTransitionMaskImage = backButtonImage;

8. 导航栏变化

1)导航栏高度变化

导航栏在iOS10之前都是默认的64p,但是,到了iOS10就不单单是64p了,可以看一下系统的信息App,在iOS11添加了大标题,效果如下图:


968977-410f670afe737036.png
968977-410f670afe737036.png

navigationBar的结构,看图2、3、4:

图2.png
图2.png
图3.png
图3.png
图4.png
图4.png
  • 在上面三幅图可以知道,在iOS11导航栏多了一个LargeTitleView,专门显示大字标题用的,整个导航栏的高度达到了96p,这不包括状态栏的高度,也就是说,整个app顶部高度达到了116p,其中statusbar=20,title=44,largetitle=52,不过默认是64p;当然,iPhoneX的高度会更高点,如果不显示大字标题,顶部的高度也达到了88,statusbar=44,title=44,如果显示大字标题,则高度变成了140,statusbar=44,title=44,largetitle=52,也就是说,iPhoneX的刘海高度为24p,大字标题如下图:
iphoneX.png
iphoneX.png
iphoneX之前的机型.png
iphoneX之前的机型.png
2.导航栏的图层变化
  • iOS11之前导航栏的title是添加在UINavigationItemView上面,而navigationBarButton则直接添加在navigationBar上面;如果设置了titleView,则titleView也是直接添加在navigationBar上面,如图5:
图5.png
图5.png
  • 在iOS11之后,苹果添加了新的类来管理,navigationBar会添加在_UIButtonBarStackView上面,而_UIButtonBarStackView则添加在_UINavigationBarContentView上面;如果没有给titleView赋值,则titleView会直接添加在_UINavigationBarContentView上面,如果赋值给了titleView,则会新生成_UITAMICAdaptorView,把titleView添加在这个类上面,这个类会添加在_UINavigationBarContentView上面,如下图6、7:
图6.png
图6.png
图7.png
图7.png
3) 导航栏的边距变化
  • 在iOS11对导航栏里面的item的边距也做了调整,titleView调整最大的宽带,边距在iPhone6p上是20p,在iPhone6p以下是16p;在iOS11以下,这个边距分别是12p和8p;如果设置了左右navigationBarButton,则在iOS11里,navigationBarButton的边距是20p和16p;在iOS11以下,也是20p和16p;如果同时设置了titleView和navigationBarButton,则在iOS11以下,它们之间的间距是6p,在iOS11则无间距,如下图8、9:
图8.png
图8.png
图9.png
图9.png
4) App需要实现导航栏左右按钮边距为0
  • 在iOS11之前,可以设置一个width为负的navigationBarButton,将按钮挤到边缘,变相实现0边距的导航栏按钮,但是,这招在iOS11失效了,原因在于_UIButtonBarStackView,这个iOS9之后出来的,用来相对布局的组件,限制了子view的布局。那怎么搞呢?
  • 设置titleView,然后将button添加在titleView上面,根据不同的边距做偏移。
  • 这个做法完全可以做到0边距,但是,问题来了,就是点击区域的问题。因为左右navigationBarButton的点击区域是超出父view的,所以,点击不到。这好办,重写titleView的hitTest方法就好。嘿嘿嘿,问题没有那么简单。之前在iOS11的图层结构就解释过,titleView会被添加在_UITAMICAdaptorView上面,而重点是,这个view也有边距,所以,单单重写titleView的hitTest方法还不够,那怎么解决呢?我的办法就是写一个view的类别,hook所有view的hitTest方法,在里面判断是否是iOS11以上,是否是_UITAMICAdaptorView类,如果都满足条件,则可以搞事了。
5) UIBarItem

在UINavigationBar中新增了一个BOOL属性prefersLargeTitles,将该属性设置为ture,navigation bar就会在整个APP中显示大标题,如果想要在控制不同页面大标题的显示,可以通过设置当前页面的navigationItem的largeTitleDisplayMode属性;

UINavigationController和滚动交互

滚动的时候,以下交互操作都是由UINavigationController负责调动的:

1.UIsearchController搜索框效果更新

2.大标题效果的控制

3.Rubber banding效果 //当你开始往下拉,大标题会变大来回应那个滚轮

所以,如果你使用navigation bar,组装push和pop体验,你不会得到searchController的集成、大标题的控制更新和Rubber banding效果,因为这些都是由UINavigationController控制的。

9. iPhoneX底部tabbar的高度改变

iPhoneX不止多了刘海,底部还有一个半角的矩形,使得tabbar多出来了34p的高度,不过不管导航栏和tabbar一般系统都会自动适配safeArea。


iphoneX tabbar.png
iphoneX tabbar.png

10. Table Views:separatorInset 扩展

  • iOS 7 引入separatorInset属性,用以设置 cell 的分割线边距,在 iOS 11 中对其进行了扩展。可以通过新增的UITableViewSeparatorInsetReference枚举类型的separatorInsetReference属性来设置separatorInset属性的参照值。
typedef NS_ENUM(NSInteger, UITableViewSeparatorInsetReference) {  
UITableViewSeparatorInsetFromCellEdges,   //默认值,表示separatorInset是从cell的边缘的偏移量
UITableViewSeparatorInsetFromAutomaticInsets  //表示separatorInset属性值是从一个insets的偏移量
}

11. 管理margins 和 insets

layout margins
基于约束的Auto Layout,使我们搭建能够动态响应内部和外部变化的用户界面。Auto Layout为每一个view都定义了margin。margin指的是控件显示内容部分的边缘和控件边缘的距离。可以用layoutMargins
或者layoutMarginsGuide属性获得view的margin,margin是视图内部的一部分。layoutMargins允许获取或者设置UIEdgeInsets结构的margin。layoutMarginsGuide则获取到只读的UILayoutGuide对象。
在iOS11新增了一个属性:directional layout margins,该属性是NSDirectionalEdgeInsets结构体类型的属性:

typedef struct NSDirectionalEdgeInsets { CGFloat top, leading, bottom, trailing;} NSDirectionalEdgeInsets API_AVAILABLE(ios(11.0),tvos(11.0),watchos(4.0));

layoutMargins是UIEdgeInsets结构体类型的属性:

typedef struct UIEdgeInsets { CGFloat top, left, bottom, right;} UIEdgeInsets;

从上面两种结构体的对比可以看出,NSDirectionalEdgeInsets属性用leading 和 trailing 取代了之前的 left 和 right。
directional layout margins属性的说明如下:

directionalLayoutMargins.leading is used on the left when the user interface direction is LTR and on the right for RTL. Vice versa for directionalLayoutMargins.trailing.

例子:当你设置了trailing = 30;当在一个right to left 语言下trailing的值会被设置在view的左边,可以通过layoutMargin的left属性读出该值。如下图所示:




还有其他一些更新。自从引入layout margins,当将一个view添加到viewController时,viewController
会修复view的的layoutMargins
为UIKit定义的一个值,这些调整对外是封闭的。从iOS11开始,这些不再是一个固定的值,它们实际是最小值,你可以改变view的layoutMargins为任意一个更大的值。而且,viewController新增了一个属性:viewRespectsSystemMinimumLayoutMargins,如果你设置该属性为"false",你就可以改变你的layoutMargins为任意你想设置的值,包括0,导航栏TitileView的宽度设置
在导航titleView使用SearchBar宽度适配问题?如果您在Navigation上的titleView上添加searchBar,iOS11情况下可能有问题,如下图所示:

12 .Xcode9下相册等访问权限问题

之前项目中相机功能一直使用系统自带的PickerView,说实话不甚美观,自己空闲之余一直着手开发自定义相机(EVNCamera:给个StarO(∩_∩)O~)。在Xcode9的首个Beta版本中开发相机功能时发现,原有项目竟然crash,后来发现iOS11下,苹果对相册的权限key做了调整,原来的 NSPhotoLibraryUsageDescription ,在iOS11之后,改成了NSPhotoLibraryAddUsageDescription。详见:Cocoa Keys

13.近场通讯NFC权限

在iOS11中,苹果开放了NFC(Near field communication),怕也是其推广ApplePay的一种策略。在使用近场通讯时,首先也要在info.plist配置NFCReaderUsageDescription 权限,案例步骤,如下:
iOS 11 Core NFC - any sample code?



作者:VIVILIA
链接:http://www.jianshu.com/p/a4e778c2236e
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

猜你喜欢

转载自blog.csdn.net/u014220518/article/details/78112614