iOS11及Xcode9适配问题汇总

UIScrollView and UITableView的新特性

ScrollView

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

新增的contentInsetAdjustmentBehavior属性用来配置adjustedContentInset的行为,该结构体有如下几种类型:数组

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;

UIScrollViewContentInsetAdjustmentBehavior 是一个枚举类型,值有如下几种:缓存

  • automatic 和scrollableAxes同样,scrollView会自动计算和适应顶部和底部的内边距而且在scrollView 不可滚动时,也会设置内边距.
  • scrollableAxes 自动计算内边距.
  • never不计算内边距
  • always 根据safeAreaInsets 计算内边距

TableView

1.UITableview UICollectionView MJRefresh下拉刷新错乱或是莫名有20空隙的问题

if (@available(iOS 11.0, *)) {
    _tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
    _tableView.contentInset = UIEdgeInsetsMake(64, 0, 49, 0);//iPhoneX这里是88
    _tableView.scrollIndicatorInsets = _tableView.contentInset;
}

若是改完后运行无效果 能够尝试Clean一下工程再运行, 不少小伙伴都遇到这样的问题了, 你不妨也试以试.安全

2.在iOS 11中默认启用Self-Sizing 未使用AutoLayout的TableView中的高度会出现问题.

Self-Sizing在iOS11下是默认开启的,Headers, footers, and cells都默认开启Self-Sizing,全部estimated 高度默认值从iOS11以前的 0 改变为UITableViewAutomaticDimension.app

若是目前项目中没有使用estimateRowHeight属性,在iOS11的环境下就要注意了,由于开启Self-Sizing以后,tableView是使用estimateRowHeight属性的,这样就会形成contentSizecontentOffset值的变化,若是是有动画是观察这两个属性的变化进行的,就会形成动画的异常,由于在估算行高机制下,contentSize的值是一点点地变化更新的,全部cell显示完后才是最终的contentSize值。由于不会缓存正确的行高,tableView reloadData的时候,会从新计算contentSize,就有可能会引发contentOffset的变化。iOS11下不想使用Self-Sizing的话,能够经过如下方式关闭:ide

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

3.TableView的separatorInset扩展

iOS 7 引入separatorInset属性,用以设置 cell 的分割线边距,在 iOS 11 中对其进行了扩展。能够经过新增的UITableViewSeparatorInsetReference枚举类型的separatorInsetReference属性来设置separatorInset属性的参照值.测试

经过下面的参考图能够看出他们的区别:动画

4. TableView和SafeArea(安全区)

有如下几点须要注意:ui

  • separatorInset 被自动地关联到 safe area insets,所以,默认状况下,表视图的整个内容避免了其根视图控制器的安全区域的插入。
  • UITableviewCell 和 UITableViewHeaderFooterView的 contentview 在安全区域内;所以你应该始终在 contentview 中使用add-subviews操做。
  • 全部的 headers 和 footers 都应该使用UITableViewHeaderFooterView,包括 table headers 和 footers、section headers 和 footers。

5. TableView的滑动操做

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

这两个代理方法返回的是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;
}
typedef NS_ENUM(NSInteger, UIContextualActionStyle) {
    UIContextualActionStyleNormal,
    UIContextualActionStyleDestructive
} NS_SWIFT_NAME(UIContextualAction.Style)

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

typedef NS_ENUM(NSInteger, UIContextualActionStyle) {
    UIContextualActionStyleNormal,
    UIContextualActionStyleDestructive
} NS_SWIFT_NAME(UIContextualAction.Style)

滑动操做这里还有一个须要注意的是,当cell高度较小时,会只显示image,不显示title,当cell高度够大时,会同时显示image和title。我写demo测试的时候,由于每一个cell的高度都较小,因此只显示image,而后我增长cell的高度后,就能够同时显示image和title了。见下图对比:

iOS11中 UIKit’s Bars 上的变化

WWDC经过iOS新增的文件管理App:Files开始介绍,在Files这个APP中可以看到iOS11中UIKit’s Bars的一些新特性:在浏览功能上的大标题视图(向上滑动后标题会回到原来的UI效果)、横屏状态下tab上的文字和icon会变为左右排列:

竖屏

横屏

在iPhone上,tab上的图标较小,tab bar较小,这样垂直空间可多放置内容。若是有人看不清楚tab bar上的图标或文字,能够经过长按tab bar上的任意item,会将该item显示在HUD上,这样能够清楚的看清icon和text。对tool bar 和 navigation bar同理,长按item也会放大显示.

  • UIBarItem

UIBarItem是UI tab bar item和UI bar button item的父类,要想实现上面介绍的效果,只须要为UIBarItem 设置landscapeImagePhone属性,在storyboard中也支持这个设置,对于HUD的image须要设置另外一个iOS11新增的属性:largeContentSizeImage,关于这部分更详细的讨论,能够参考 WWDC2017 Session 215:What's New in Accessibility

  • 控制大标题的显示

UINavigationbar中新增了一个BOOL属性prefersLargeTitles,将该属性设置为ture,navigationbar就会在整个APP中显示大标题,若是想要在控制不一样页面大标题的显示,能够经过设置当前页面的navigationItemlargeTitleDisplayMode属性.

navigationItem.largeTitleDisplayMode 

typedef NS_ENUM(NSInteger, UINavigationItemLargeTitleDisplayMode) {  
/// 自动模式依赖上一个 item 的特性
UINavigationItemLargeTitleDisplayModeAutomatic,
/// 针对当前 item 老是启用大标题特性
UINavigationItemLargeTitleDisplayModeAlways,
/// Never 
UINavigationItemLargeTitleDisplayModeNever,
}

把你的UISearchController赋值给navigationItem,就能够实现将UISearchController集成到 Navigation.

navigationItem.searchController  //iOS 11 新增属性
navigationItem.hidesSearchBarWhenScrolling //决定滑动的时候是否隐藏搜索框;iOS 11 新增属性

UINavigationController和滚动交互

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

UIsearchController搜索框效果更新
大标题效果的控制
Rubber banding效果 //当你开始往下拉,大标题会变大来回应那个滚轮

因此,若是你使用navigation bar,组装一些总体push和pop体验,你不会获得searchController的集成、大标题的控制更新和Rubber banding效果,由于这些都是由UINavigationController控制的。

Margins 和 Insets

基于约束的Auto Layout, 使咱们搭建可以动态响应内部和外部变化的用户界面. Auto Layout为每个view都定义了marginmargin指的是控件显示内容部分的边缘和控件边缘的距离.
能够用layoutMargins或者layoutMarginsGuide属性得到viewmarginmargin是视图内部的一部分. layoutMargins容许获取或者设置UIEdgeInsets结构的marginlayoutMarginsGuide则获取到只读的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));

layoutMarginsUIEdgeInsets结构体类型的属性:

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的左边, 能够经过layoutMarginleft属性读出该值. 以下图所示:

还有其余一些更新. 自从引入layout margins, 当将一个view添加到viewController时, viewController会修改viewlayoutMargins为UIKit定义的一个值, 这些调整对外是封闭的. 从iOS11开始, 这些再也不是一个固定的值, 它们实际是最小值, 你能够改变viewlayoutMargins为任意一个更大的值. 并且, viewController新增了一个属性: viewRespectsSystemMinimumLayoutMargins, 若是你设置该属性为false, 你就能够改变你的layoutMargins为任意你想设置的值, 包括0, 以下图所示:

相关文章
相关标签/搜索