关于自动布局和iPhone X、iOS11的适配

自从提供了Auto Layout之后,官方文档中就建议开发者尽量的在布局中使用自动布局技术,虽然使用frame布局可以应付一些屏幕尺寸不是很多的设备,但是遇到iPad中可以进行多界面操作、iPhone X安全区内布局这样的迭代出来的适配问题,以前的frame布局会越来越不实用,对界面的掌控力度越来越弱,因此下面所有讨论都是基于使用Auto Layout布局而不是frame布局。

1.安全区域

危险区:传感器区域

在新机型iPhone X中由于全面屏的特性,顶部和底部分别有两个特别的传感器,顶部是一个凹形,底部则是一个短矩形。Apple的指导规则是,所有的控件都要在safeArea内,如果有可能,尽量在safeAreaMargin内以增加横屏下的控件的误操作间距。比如下面这个在标签管理界面的底部操作按钮就应该这样进行适配:

1
2
3
4
5
6
7
8
9
10
11
12
[finishButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.and.right.mas_equalTo(self.view);
make.height.mas_equalTo(49);
if (@available(iOS 11.0, *)) {
make.bottom.mas_equalTo(self.view.mas_safeAreaLayoutGuideBottom);
} else {
make.bottom.mas_equalTo(self.view.mas_bottom);
}
// 或者直接使用viewController的topLayoutGuide、bottomLayoutMargin,
// 不过这个属性在iOS11已经被废弃了,推荐使用UIView的安全区域
// make.bottom.mas_equalTo(self.mas_bottomLayoutGuideTop);
}];

同理,顶部的适配也是类似的,只不过是bottom和top的区别而已。

安全区域下的Statusbar和Tabbar

由于传感器的存在,iPhone X机型上会有一些危险区域,这些区域内部是不允许有任何app内部的可操作控件,相应的为了适应safeArea,状态栏和Tabbar的高度在iPhone X上也有所改变。

如图可以看出来,在iPhone X机型下,状态栏增加了24px (@2x),Tabbe增加了34px (@2x)。

UIView的安全区域

自定义的视图添加到控制器中,在iOS11以前由于没有安全区域的概念,所以,一般的布局没有问题,但是到了iPhone X中,显示会有一些偏差,具体就是:上面太靠上、下面太贴下。

全屏显示的界面

我们的项目中有许多地方是需要全屏显示。针对于全屏显示的视图,其布局可以使用frame根据屏幕尺寸的大小进行设置rect;也可以使用自动布局进行布局,但是,约束就不再跟safe area有关了,可以直接根据superViewtopbottom来设置约束。比如右边的这个侧滑出来的效果,整个模糊视图是添加在控制器上的,然后上面的控件添加到这个全屏显示的视图上。

虽然对于全屏视图不需要使用safe area,但是,其子视图则需要使用safe area来适配iPhone X机型。具体适配可以参考上面适配底部传感器中的方式:

1
2
3
4
5
6
7
8
9
[self.bottomSubview mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.and.right.mas_equalTo(superView);
make.height.mas_equalTo(49);
if (@available(iOS 11.0, *)) {
make.bottom.mas_equalTo(superView.mas_safeAreaLayoutGuideBottom).mas_offset(-20);
} else {
make.bottom.mas_equalTo(superView.mas_bottom).mas_offset(-20);
}
}];

半屏幕显示的界面

项目中比较多的是直播间从底部弹出一些视图,这些视图有的为展示类型,有的为可操作类型,展示类型中又分为表视图可滚动以及标签静态展示。对于表视图,Apple的文档中说可以设置底部紧贴父视图的底部,UIKit会为底部传感器遮挡的部分流出来间距,而如果是标签或者按钮之类的非滚动控件,则需要开发人员自行进行安全区域的适配。

这部分我的设计建议是对这些半屏可弹出视图进行重新设计:

  • 对于弹出视图,让他们都全屏显示

  • 然后添加遮罩视图提供毛玻璃、无颜色、透明黑色等显示效果,并且具备点击隐藏的响应事件

  • 内部添加需要展示的内容视图,根据需要展示的具体内容撑起整个内容视图

因为有的具体内容需要避开底部的传感器,因此会有不同版本之间的适配。或者不使用遮罩视图,让弹出视图自己成为遮罩视图。如右所示:

对弹出视图做约束:

1
2
3
4
5
BottomFollowView * followView = [BottomFollowView followView];
[self.view addSubview:followView];
[followView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.mas_equalTo(self.view);
}];

依照上面的设计规则,对内容视图做约束:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[_contentView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.mas_equalTo(self);
make.bottom.mas_equalTo(self);
make.top.mas_equalTo(_followButton.mas_top).mas_offset(-20);
}];

[_followButton mas_makeConstraints:^(MASConstraintMaker *make) {

CGFloat margin = 10;
make.size.mas_equalTo(CGSizeMake(120, 40));
make.centerX.mas_equalTo(_contentView);
if (@available(iOS 11.0, *)) {
make.bottom.mas_equalTo(_contentView.mas_safeAreaLayoutGuideBottom).mas_offset(0);
} else {
make.bottom.mas_equalTo(_contentView.mas_bottom).mas_offset(-margin);
}
}];

Xib中的安全区域

难保一些界面使用xib进行构建,这些界面中有部分需要适配iPhone X,就需要用到安全区域。在xib中打开安全区域的办法很简单,由于这个是iOS 11中添加的,所以要求Xcode版本至少9.0以上,如图:

打开使用safeAreaLayoutGuide之后,子视图就可以根据安全区域来进行设置约束了。在设置约束的时候不再是根据superView来进行布局,而是根据safe area

这样设置之后,实测在低版本(iOS 11以下)中,也不会出现系统不匹配运行崩溃的问题,应该是UIKit内部做了适配。但是如果项目最低版本为iOS 8.0,则不可以在xib中使用safe area功能。

2.导航栏

在iOS11之后新增了一个lagreTitle属性,就是大标题,默认是不开启的,也可以选择在那一个控制器中开启,由于我们的项目中暂时没有用到大标题,所以没有适配需求。

1
2
3
4
5
6
7
8
9
10
11
@interface UINavigationBar
/// 当设置为YES的时候,导航栏支持使用大标题,对于特殊的不需要显示大标题的控制器,
/// 可以通过设置UINavigationItem.largeTitleDisplayMode的值来决定 。默认是 NO.
@property (nonatomic, readwrite, assign) BOOL prefersLargeTitles;
@end

@interface UINavigationItem
/// 当 UINavigationBar.prefersLargeTitles=YES的时候来决定当前控制器的大标题如何显示。
/// 如果 prefersLargeTitles=NO的时候,大标题无效。默认值是 Automatic
@property (nonatomic, readwrite, assign) UINavigationItemLargeTitleDisplayMode largeTitleDisplayMode;
@end

对于返回按钮,项目中导航栏的返回效果是一个返回箭头,没有文字。在iOS10之前使用自定义的返回按钮是没有问题的,不过到了iOS11之后,返回按钮就是这个样子,有一个向下大约10px的位移。不过在新建一个工程之后,却没有这样的情况发生,有可能是老版本遗留下的问题,不过这都可以使用下面的自定义返回按钮进行解决。

目前有两种不同系统下的四种情况:

使用系统提供的返回按钮

可以看出来,在iOS11以前,系统只是使用了两个简单的类:UINavigationItemButtonView_UINavigationBarBackIndicatorView来展示返回按钮和前一个界面的title。而到了iOS11,则使用了更多的类来进行返回按钮的组装,应该是由于在iOS 11之后,导航栏也支持自动布局了,简单的两个类不能够适用于多种场景,增加了一些容器类:_UINavigationBarContentView_UIButtonBarStackView等。

使用自定义的返回按钮

而如果使用自定义返回按钮,在iOS11系统下,系统提供了一些StackView来实现对自定义按钮的自动布局约束,具体为,navigationBar会添加在_UIButtonBarStackView上面,而_UIButtonBarStackView则添加在_UINavigationBarContentView上面。

title和titleView

导航栏中的titletitleView在iOS11 以后也发生了变化。

由于我们不需要自定义titleView,因此这部分没有什么适配问题。

3.UIScrollView

iOS11中废弃了automaticallyAdjustsScrollViewInsets,取而代之的是contentInsetAdjustmentBehavior属性和adjustedContentInset属性决定UIScrollView与边缘的距离。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@interface UIViewContrller

@property(nonatomic,assign) BOOL automaticallyAdjustsScrollViewInsets API_DEPRECATED_WITH_REPLACEMENT("Use UIScrollView's contentInsetAdjustmentBehavior instead", ios(7.0,11.0),tvos(7.0,11.0)); // Defaults to YES
@end

@interface UIScrollView

/* 当contentInsetAdjustmentBehavior允许时,UIScrollView可以合并
safeAreaInsets放入adjustedContentInset中。
*/
@property(nonatomic, readonly) UIEdgeInsets adjustedContentInset;

/* 配置 adjustedContentInset
默认是 UIScrollViewContentInsetAdjustmentAutomatic.
*/
@property(nonatomic) UIScrollViewContentInsetAdjustmentBehavior contentInsetAdjustmentBehavior API_AVAILABLE(ios(11.0),tvos(11.0));

@end

typedef NS_ENUM(NSInteger, UIScrollViewContentInsetAdjustmentBehavior) {
UIScrollViewContentInsetAdjustmentAutomatic, // 类似于.scrollableAxes,但是为了向后兼容性,当滚动视图由视图控制器拥有时,也会调整顶部和底部contentInset,在导航控制器中自动调用ScrollViewInsets = YES,而不管滚动视图是否可滚动
UIScrollViewContentInsetAdjustmentScrollableAxes, // 可滚动轴的边缘被调整(即,contentSize.width / height> frame.size.width / height或alwaysBounceHorizontal / Vertical = YES)
UIScrollViewContentInsetAdjustmentNever, // contentInset 不适应内边距
UIScrollViewContentInsetAdjustmentAlways, // contentInset 永远会适应scrollView的safeAreaInsets
} API_AVAILABLE(ios(11.0),tvos(11.0));

UITableView

UITableView在iOS 11中默认启用Self-Sizing。在iOS8引入Self-Sizing 之后,我们可以通过实现estimatedRowHeight相关的属性来展示动态的内容,实现了estimatedRowHeight属性后,得到的初始contentSize是个估算值,是通过(estimatedRowHeight x cell的个数)得到的,并不是最终的contentSizetableView不会一次性计算所有的cell的高度了,只会计算当前屏幕能够显示的cell个数再加上几个,滑动时,tableView不停地得到新的cell,更新自己的contenSize,在滑到最后的时候,会得到正确的contenSize。如果底部区域不存在可交互的固定组件,那么tableView需要延伸到屏幕底部,而不是安全区域以内,UIKit会为滚动视图提供一个安全的contentOffset。

由于我们的项目中没有使用self-sizing,基本上都是固定或者计算动态高度,所以需要关闭默认开启的Self-Sizing

1
2
3
self.tableView.estimatedRowHeight = 0;
self.tableView.estimatedSectionHeaderHeight = 0;
self.tableView.estimatedSectionFooterHeight = 0;

UITableView除了在iOS11下自动开启了Self-Sizing,还对UITableViewCellcontentView进行了安全区域的修改。在竖屏情况下是不会有影响的,在横屏下,由于左右的传感器区域,UIKit会将contentView内嵌入安全区域内,如果不需要内嵌到安全区域,可以手动关闭这个特性,使用UITableViewinsetsContentViewsToSafeArea属性来进行控制:

1
@property (nonatomic) BOOL insetsContentViewsToSafeArea API_AVAILABLE(ios(11.0), tvos(11.0)); // default value is YES。

4.自动布局

如果使用Frame设置用户界面,必须计算视图层次结构中每个视图的大小和位置,然后,当发生变化(比如旋转屏幕、iPad多屏幕任务等),则必须重新计算所有受影响的视图的位置。由于必须自己管理所有更改,因此,设计一个简单的用户界面需要花费大量的精力进行设计,调试和维护,创建一个真正的自适应用户界面增加了一个数量级的困难。

自动布局(Auto Layout)使用一系列约束来定义用户界面。约束通常代表两个视图之间的关系。自动布局然后基于这些约束来计算每个视图的大小和位置。这产生了动态响应内部和外部变化的布局效果。

一个约束的意义

视图层次结构的布局被定义为一系列线性方程式。每个约束表示一个单一的等式。设置约束的目的是书写一系列只有一个可能解决方案的方程。

Auto Layout经常提供多种方法来解决同样的问题。理想情况下,应该选择最清楚地描述设置约束的方案。但是,不同的开发人员会有不同的设置约束习惯。官方文档推荐使用以下经验法则:

  • 整数乘法器比分数乘法器更容易理解
  • 正常数比负常数更好
  • 设置约束的时候最好有一个固定的顺序:首尾,上下

优先级

您也可以创建可选约束,所有约束条件的优先级都在1到1000之间。优先级为1000的约束条件是必需的,所有其他限制是可选的。

设计约束的解决方案时,自动布局尝试按优先级顺序从最高到最低来满足所有约束条件。如果它不能满足可选约束,则跳过该约束并继续到下一个约束。

不要觉得有义务使用全部1000个优先级值。事实上,优先级应该围绕系统定义的

  • 低(250)
  • 中(500)
  • 高(750)
  • 必需(1000)

优先级进行。可能需要制定高于或低于这些值一个或两个点的约束,以解决约束的时候出现的多重约束问题。如果超出这个范围,你可能想要重新检查布局的逻辑。

边距

在使用Xib创建视图并使用自动布局的时候,子视图相对于父视图会有一个间距,兄弟视图之间也有一个间距,这个间距有时候并不是我们想要的。

NSLayoutConstraint

NSLayoutConstraint就是上面对一个约束的抽象类,可以根据这个类的实例进行两个视图的约束设置,遵循

item1.attribute1 = multiplier × item2.attribute2 + constant

公式。

另外,约束不限于平等关系。它们也可以使用大于或等于(> =)小于或等于(<=)来描述两个属性之间的关系。制约因素也有优先级在1和1,000之间。

这种是Auto Layout框架下最基础的约束设置,但也是书写起来最麻烦的约束设置。

NSLayoutAnchor

上面的布局方程式简单的介绍了一个约束其实是什么,NSLayoutAnchor这个类就是可以简化布局方程式的一个工厂类,因此他可以极大的简化创建NSLayoutConstraint的过程。

UIView不提供布局边距属性的锚定属性。相反,有一个layoutMarginsGuide属性提供了一个UILayoutGuide的对象代表这些边距,使用layoutGuide的锚点属性来创建您的约束。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 使用 NSLayoutConstraint 创建约束
[NSLayoutConstraint
constraintWithItem:subview
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeLeadingMargin
multiplier:1.0
constant:0.0].active = YES;

[NSLayoutConstraint
constraintWithItem:subview
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeTrailingMargin
multiplier:1.0
constant:0.0].active = YES;

// 使用 Layout Anchors 创建同样的约束
UILayoutGuide *margin = self.view.layoutMarginsGuide;
subView.translatesAutoresizingMaskIntoConstraints = NO;

[subview.leadingAnchor constraintEqualToAnchor:margin.leadingAnchor].active = YES;
[subview.trailingAnchor constraintEqualToAnchor:margin.trailingAnchor].active = YES;

但是在使用NSLayoutAnchor的时候,需要注意一点,记得将视图的translatesAutoresizingMaskIntoConstraints属性设置为NO,意思是视图使用autolayout。

默认情况下,视图上的自动调整掩码会产生完全确定的约束视图的位置。这允许自动布局系统跟踪其视图的帧布局是手动控制的(例如通过-setFrame:)。
当您选择通过添加自己的约束来使用自动布局来定位视图时,您必须将此属性设置为NO。 IB将为你做这个。

@property(nonatomic) BOOL translatesAutoresizingMaskIntoConstraints NS_AVAILABLE_IOS(6_0); // Default YES

虽然文档上说的UIView不会提供布局的边距锚点,可以通过layoutMarginGuide来获取,但是通过这个属性可以看出来,这样的布局是有一个margin的,默认是20。如果不使用layoutMarginGuide,直接使用self.view.leftAnchor也是可以进行锚点布局,的这时候就不会有一个20的间距了。

1
2
3
4
5
UILayoutGuide *margin = self.view.layoutMarginsGuide;
[redView_layoutAnchor.leftAnchor constraintEqualToAnchor:margin.leftAnchor constant:0].active = YES;

// 这两个设置的效果是一样的,都是距离父视图右边局 20,区别就是没有使用 layoutMarginsGuide 属性
[redView_layoutAnchor.leftAnchor constraintEqualToAnchor:self.view.leftAnchor constant:20].active = YES;

UILayoutGuide

这是iOS9.0以后新增的类,用来对使用Auto Layout布局的时候提供虚拟占位,不会渲染到视图层级结构中。

这个类设计的巧妙的地方就在于他不会渲染到视图层级中,却可以决定有关联的视图之间的布局。

想象一个场景:三个视图,宽高一样,但是要在父视图中等间距的排列,这是一个很常遇到的需求。

如果是使用Frame来布局,那就简单了,仅仅是计算问题。但如果是自动布局,那就要增加一些『辅助视图』,『辅助视图』不会显示出来,但是会对布局有帮助,听起来和UILayoutGuide的作用一样,但是辅助视图的缺点就是会渲染到视图层级结构中。现在如果使用UILayoutGuide来实现,这很『简单』。

看起来需要写很多样板代码,如果使用Masonry来进行自动布局,就会减少很多代码。

UILayoutGuide更多的是用在UIView的自动布局中,类似的UIViewController中的topLayoutGuidebottomLayoutGuide已经废弃,替换的是使用UIView中的safeAreaLayoutGuide获取layoutGuide:

Visual Format Language

Apple在布局方面为方便实现自动布局做的一个语法糖,作用类似于上面的NSLayoutAnchor,可以简洁直观的设置约束。

UIStackView

很不巧,这个也是在iOS 9以后提供的。这个类为布局提供了简单的方式:对齐方式、排列方式、填充方式等。从上面导航栏那一节可以知道,在iOS11中导航栏使用自动布局,就是使用的UIStackView的私有子类,而且在自动布局指南中,也有提到UIStackView内部是使用的自动布局技术。

5.布局相关的方法

无论使用frame设置子视图的布局,还是使用自动布局设置,不同的人会有不同的方法在不同的地方设置。比如,在init或者initWithFrame:方法中、在layoutSubviews中、在viewDidLoad中、在viewWillLayoutSubviews中。需要注意的是,如果使用frame进行布局,有的地方是拿不到正确的视图尺寸的,那么这些方法又是在生命周期中什么时候调用,具体又是什么含义,下面,摘抄记录一下自动布局指南中关于设置、更新约束的时机和方法。

更改约束

以下所有操作都会改变一个或多个约束条件:

  • 激活或停用约束
  • 更改约束的常量值
  • 更改约束的优先级
  • 从视图层次结构中移除视图

其他更改(如设置控件属性或修改视图层次结构)也可以更改约束。发生更改时,系统将进行延期布局。

一般来说,可以随时进行这些更改。理想情况下,大多数约束条件应该在Interface Builder中设置,或者在控制器的初始设置(例如, viewDidLoad)期间由视图控制器以编程方式创建。如果需要在运行时动态更改约束,通常最好在应用程序状态更改时更改它们。例如,如果想要更改约束来响应按钮点击,请直接在按钮的操作方法中进行更改约束。

延期布局(The Deferred Layout Pass)

自动布局不是立即更新受影响的视图的框架,而是安排不久的将来布局。先延迟传递更新布局的约束,然后计算视图层次结构中所有视图的frame。

可以通过调用setNeedsLayout方法或setNeedsUpdateConstraints方法来主动的进行自己的延期布局。

延期布局过程实际上涉及两个修改视图层次的过程:

  1. 更新过程根据需要更新约束
  2. 布局过程根据需要重新定位视图的frame
更新过程(Update Pass)

系统遍历视图层次,并调用所有视图控制器上的updateViewConstraints,以及所有视图上的updateConstraints方法。可以重写这些方法来优化对约束的更改,比如下面的批量更改。

布局过程(Layout Pass)

系统再次遍历视图层次,并调用所有视图控制器上的viewWillLayoutSubviews,并在所有视图上调用layoutSubviews。默认情况下,该layoutSubviews方法中可以使用Auto Layout引擎计算的出来的矩形来更新每个子视图的框架。可以覆盖这些方法来修改布局。

可以看出来,视图的布局中主要的函数调用顺序为:

updateConstraints -> layoutSubViews -> drawRect

批量更改(Batching Changes)

在发生影响变化之后,立即更新约束几乎总是更清晰和更容易。将这些更改推迟到以后的方法会使代码更复杂,更难理解。

但是,出于性能方面的原因,有时可能需要批量更改。这应该只在更改约束的地方太慢,或者当一个视图正在进行一些冗余的更改时才能完成。

要批量更改,而不是直接进行更改,请调用 setNeedsUpdateConstraints包含约束的视图上的方法。然后,重写视图的updateConstraints方法来修改受影响的约束。

注意updateConstraints方法必须尽可能高效。在这个方法中不要停用所有约束,然后重新激活所需的约束。相反,应用程序必须有一些方法来跟踪约束,并在每次更新过程中验证它们,只更改需要更改的项目。在每次更新过程中,都必须确保对应用程序的当前状态有适当的限制。

始终将调用父类方法放置在最后一步。不要在updateConstraints方法中调用setNeedsUpdateConstraints。调用setNeedsUpdateConstraints会开启另一个更新通道,导致反馈循环。

Auto Layout指南中的自定义布局

应该重写viewWillLayoutSubviewslayoutSubviews方法来修改布局引擎返回的结果。

如果可能的话,使用约束来定义所有的布局。生成的布局更健壮,更易于调试。当您需要创建无法单独使用约束表达的布局时,您应该只覆盖viewWillLayoutSubviewslayoutSubviews方法。

覆盖这些方法时,布局处于不一致的状态。已经有一些意见。其他人没有。您需要非常小心如何修改视图层次结构,或者您可以创建反馈循环。以下规则可以避免反馈循环:

  • 调用超类的方法
  • 在调用超类方法之前可以设置子视图中的布局无效,且必须是在调用超类方法之前
  • 不要使你的子树以外的任何视图的布局失效。这可能会创建一个反馈循环
  • 不要调用setNeedsUpdateConstraints函数,因为刚刚完成更新通行证,调用这个方法会导致反馈循环
  • 不要调用setNeedsLayout函数,调用这个方法会创建一个反馈循环
  • 要小心改变限制。你不想意外地使你的子树外的任何视图的布局失效

Masonry中建议设置布局的位置

在Masonry的README中可以看到,他们建议创建约束的位置为下图所示,

和上面文档中所说的一致,只不过他们选择的是在更新过程中来进行设置约束,而且是建议remakeupdate。另外,为了告诉UIKit这个视图是使用的Auto Layout,重写了+ (BOOL)requiresConstraintBasedLayout函数,返回YES,这个和在外面设置translatesAutoresizingMaskIntoConstraints=NO来禁用Autoresizing Masks是一样的。

6.远古布局技术Autoresizing

在iOS2.0系统中引入的用于屏幕适配的技术。但是仅仅适用于子视图和父视图之间的布局关系,这项技术的作用是,当父视图的bounds发生变化的时候,如何自动调整子视图的布局。表现形式在xib中是使用6个线,在code中使用一个UIView的枚举属性autoresizingMask

1
2
3
4
5
6
7
8
9
typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
UIViewAutoresizingFlexibleTopMargin = 1 << 3,
UIViewAutoresizingFlexibleHeight = 1 << 4,
UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};

另外,对于一个视图来说,是否使用autoresizing技术可以通过autoresizingSubviews这个布尔属性来决定。一句话就是,autoresizingSubviews决定是否使用,autoresizingMask决定如何使用。

1
2
@property(nonatomic) BOOL autoresizesSubviews;// default is YES
@property(nonatomic) UIViewAutoresizing autoresizingMask;// default is UIViewAutoresizingNone

具体前面提到的6根线是什么,可以参考下面的文章链接

在这个说AutoLayout的文章里面提及Autoresizing主要是因为他们两个不能共存,或者说AutoLayout是相对于Autoresizing更加完备的布局技术,Autoresizing只能指明父视图改变bounds的时候如何去适应子视图,却没有办法决定子视图之间如何适应改变,而AutoLayout却可以做到这些。Xcode5之后在xib、sb内加入了autolayout的选项开关,并且默认是开启的,这也是Apple推荐使用AutoLayout,如果不想使用AutoLayout仍然想使用Autoresizing,关闭autolayout的选项开关即可。

另外说一下,如果项目中有以前使用xib布局的类在设备上显示的时候有frame相关的bug,排查过没有主动修改frame,那么百分之八九十都是由于Autoresizing引起的。


参考文章

人机界面指南 - 官方

自动布局指南 - 官方

你可能需要为你的APP适配iOS11 - 简书

针对iPhone X的适配指南 - 官方

为 iPhone X 更新您的 app - 官方

你需要为你的APP适配iOS11 - WWDC session 204

app界面适配iOS11 - 简书

iPhoneX适配实践 - 腾讯云社区

iOS11 & iPhoneX适配总结 - 简书

iOS屏幕适配系列(一):Autoresizing技术 - 简书

一篇文章详解iOS之AutoResizing、AutoLayout、sizeClass来龙去脉 - 简书