概述
iOS11 中增加了 SafeArea 的概念,规定了苹果建议的最合适的内容展示区域。
在iPhoneX中
- 状态栏高度由 20pt 变为 44pt
- 导航栏以上高度由 64pt 变为 88pt
- 底部工具栏需要为 home indicator 留出 34pt 边距
Frame 布局
UIView
UIView 中新增了以下属性,我们可以利用此属性来知道当前 view 所需要下移以及加高的值。
@property(nonatomic, readonly) UIEdgeInsets safeAreaInsets;
- 通常在 - (void)layoutSubviews 方法中使用 safeAreaInsets 布局
- (void)layoutSubviews {
[super layoutSubviews];
if (@available(iOS 11.0, *)) {
self.titleLabel.frame = CGRectMake(0, self.view.safeAreaInsets.top, 100, 100);
}
}
UIViewController
UIViewController 中新增了以下属性来设置 safeAreaInsets
@property(nonatomic) UIEdgeInsets additionalSafeAreaInsets;
viewSafeAreaInsetsDidChange 的调用顺序:
- (void)viewDidLoad
- (void)viewWillAppear
- (void)viewSafeAreaInsetsDidChange // safeArea 改变时调用
- (void)viewWillLayoutSubviews
- (void)viewDidLayoutSubviews
- (void)viewDidAppear
- 只有在调用 viewSafeAreaInsetsDidChange 及以后的方法才能获得 view 和 Controller 的UIEdgeInsets(44,0,34,0)。所以在 viewDidLoad 中根据 safeAreaInsets 设置界面会有问题。
AutoLayout 布局
UIView 中还有一个用于 AutoLayout 布局的属性
@property(nonatomic, readonly, strong) UILayoutGuide *safeAreaLayoutGuide;
- Masonry 1.1.0 已更新了 SafeArea 相关的用法
// 尽量使用这种方式
[self.label mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(self.penBtn.mas_left);
make.top.equalTo(self.view.mas_top).priorityMedium();
if (@available(iOS 11.0, *)) {
make.top.greaterThanOrEqualTo(self.view.mas_safeAreaLayoutGuideTop);
}
}];
// 如果 iphoneX 和其他机型偏移量不同且无法用 SafeArea 相关属性解决,再用这样的方式
[view mas_makeConstraints:^(MASConstraintMaker *make) {
if ([UIDevice awe_isIPhoneX]) {
if (@available(iOS 11.0, *)) {
make.top.greaterThanOrEqualTo(self.view.mas_safeAreaLayoutGuideTop).offset(12);
}
} else {
make.top.equalTo(self.view).offset(30);
}
make.right.lessThanOrEqualTo(self.view.mas_left).offset(10);
make.centerX.equalTo(self.view);
}];
一些Tips
- 适配时尽量使用 SafeArea 相关的属性,不要简单粗暴的直接指定偏移量的大小
// 错误示范!!!不要这样搞!!!
[self.cancelButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(@(74));
make.height.equalTo(@(74));
if ([UIDevice awe_isIPhoneX]) {
make.top.equalTo(@(20));
} else {
make.top.equalTo(@(0));
}
make.right.equalTo(@(0));
}];
// 这样才是对哒~!
[self.cancelButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(@(74));
make.height.equalTo(@(74));
make.right.equalTo(@(0));
make.top.equalTo(self.view.mas_top).priorityMedium();
if (@available(iOS 11.0, *)) {
make.top.greaterThanOrEqualTo(self.view.mas_safeAreaLayoutGuideTop);
}
}];
- 常用的宏
- 一般用于 frame 的改变,AutoLayout 中尽量不要用
#define STATUS_BAR_HEIGHT [UIApplication sharedApplication].statusBarFrame.size.height
#define STATUS_BAR_NORMAL_HEIGHT ([UIDevice isIPhoneX] ? STATUS_BAR_HEIGHT : 20)
#define TAB_BAR_HEIGHT 49
#define NAVIGATION_BAR_HEIGHT ([UIDevice isIPhoneX] ? 88 : 64)
#define NAVIGATION_BAR_OFFSET ([UIDevice isIPhoneX] ? 24 : 0)
#define IPHONE_X_BOTTOM_OFFSET ([UIDevice isIPhoneX] ? 34 : 0)
- 判断机型 : 不到万不得已不要用这个方法!不到万不得已不要用这个方法!不到万不得已不要用这个方法!
#import <sys/utsname.h>
+ (BOOL)isIPhoneX {
struct utsname systemInfo;
uname(&systemInfo);
NSString *platform = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
if ([platform isEqualToString:@"iPhone10,3"] || [platform isEqualToString:@"iPhone10,6"]) {
return YES;
}
return NO;
}