应用场景1:自定义导航栏内容,导航栏显示公告和标题
应用场景2:自定义视图底部工具栏
应用场景3: 适配上拉加载更多控件 _vcView.tableView.mj_footer.ignoredScrollViewContentInsetBottom = k_ignoredScrollViewContentInsetBottom;
//推荐使用API获取高度
#define k_safeAreaInsetsBottom [UIApplication sharedApplication].delegate.window.safeAreaInsets.bottom
#define isIphoneX isHasSafeAreaInsets
#define k_ignoredScrollViewContentInsetBottom (isIphoneX?k_safeAreaInsetsBottom:0)
static inline BOOL isIPhoneXSeries() {
if (@available(iOS 11.0, *)) {
UIWindow *mainWindow = [[[UIApplication sharedApplication] delegate] window];
if (mainWindow.safeAreaInsets.bottom > 0.0) {
return YES;
}
}
return NO;
}
The safe area of a view reflects the area not covered by navigation bars, tab bars, toolbars, and other ancestors that obscure a view controller's view. (In tvOS, the safe area reflects the area not covered by the screen's bezel.) You obtain the safe area for a view by applying the insets in this property to the view's bounds rectangle. If the view is not currently installed in a view hierarchy, or is not yet visible onscreen, the edge insets in this property are 0.
safeArea
是指没有被导航栏, tabbar栏, toolbars, 或其他视图遮盖的区域。 通过safeAreaInsets
属性可以获取到视图的安全距离. 但是如果一个view没有在视图层次结构中或未在屏幕上显示, 则safeAreaInsets为0;
视图底部工具栏显示到安全区域之外
#define isIphoneX [QCT_Common isiPhoneX]
+ (BOOL)isiPhoneX {
if (@available(iOS 11.0, *)) {
if ([UIApplication sharedApplication].delegate.window.safeAreaInsets.bottom > 0) {//iphoneX横屏
return YES;
} else {
return NO;
}
} else {
return NO;
}
}
定义相应宏
///*状态栏和导航栏总高度*/
#define kStatusBarHeight (CGFloat)(isIphoneX?(88.0):(64.0))
/*iPhoneX的状态栏高度差值*/
#define kPtatusBarHeight (CGFloat)(isIphoneX?(24.0):(0))
/*底部安全区域远离高度*/
#define kDtatusBarHeight (CGFloat)(isIphoneX?(34.0):(0))
//推荐使用API获取高度
#define k_safeAreaInsetsBottom [UIApplication sharedApplication].delegate.window.safeAreaInsets.bottom
#define isIphoneX isHasSafeAreaInsets
#define k_ignoredScrollViewContentInsetBottom (isIphoneX?k_safeAreaInsetsBottom:0)
推荐使用API获取高度
#define kstatusBarH [[UIApplication sharedApplication] statusBarFrame].size.height
#define knavHeight self.navigationController.navigationBar.frame.size.height
//获取状态栏的高度
CGFloat statusHeight = [[UIApplication sharedApplication] statusBarFrame].size.height;
NSLog(@"状态栏高度:%f",statusHeight);
//获取导航栏的高度
CGFloat navHeight = self.navigationController.navigationBar.frame.size.height;
NSLog(@"导航栏高度:%f",navHeight);
//获取tabBar的高度
//1.在tabBarController中使用(你的继承自UITabBarController的VC)
CGFloat tabBarHeight = self.tabBar.frame.size.height;
NSLog(@"tabBar高度:%f",tabBarHeight);
//2.在非tabBarController中使用
UITabBarController *tabBarVC = [[UITabBarController alloc] init];//(这儿取你当前tabBarVC的实例)
CGFloat tabBarHeight = tabBarVC.tabBar.frame.size.height;
NSLog(@"tabBar高度:%f",tabBarHeight);
使用inline方式将函数在调用处强制展开,防止被hook和追踪符号。
static __attribute__((always_inline)) void anti_debug()
//一般的函数调用都会通过call的方式来调用,hacker很容易对一个函数做手脚,如果是以inline的方式编译的,会把该函数的code拷贝到每次调用该函数的地方;而static会让生成的二进制文件中没有清晰的符号表,让逆向的人很难弄清楚代码逻辑
查看汇编文件:选中xx.m文件-->Xcode 菜单 --> Product --> Perform Action --> Assemble "xx.m"
与#define的区别:
#define
宏定义的代码,编译器不会对其进行参数有效性检查,仅仅只是对符号表进行替换。#define
宏定义的代码,其返回值不能被强制转换成可转换的适合的类型。应用到本文案例
static inline BOOL isIPhoneXSeries() {
if (@available(iOS 11.0, *)) {
UIWindow *mainWindow = [[[UIApplication sharedApplication] delegate] window];
if (mainWindow.safeAreaInsets.bottom > 0.0) {
return YES;
}
}
return NO;
}
///*状态栏和导航栏总高度*/
#define kStatusBarHeight (CGFloat)(isIphoneX?(88.0):(64.0))
navView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, kWidth, kStatusBarHeight)];
自定义视图底部工具栏显示到安全区域之外
如果有安全区域距离,则视图距离底部的高度进行相应调整
[_vcView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.left.right.offset(0);
if(isHasSafeAreaInsets()){
UIWindow *mainWindow = [[[UIApplication sharedApplication] delegate] window];
CGFloat bottom = mainWindow.safeAreaInsets.bottom;
make.bottom.equalTo(weakSelf.view).offset(-bottom);
}else{
make.bottom.equalTo(weakSelf.view);
}
}];
问题:没有上拉的时候加载更多控件的文案也显示出来了
修复方式1:修改视图距离底部的高度
[self.vcView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(weakSelf.view).offset(0);
make.right.equalTo(weakSelf.view).offset(- 0);
make.top.equalTo(weakSelf.view).offset(0);
if(isHasSafeAreaInsets()){// 避免没有上拉的时候加载更多控件的文案也显示出来了
UIWindow *mainWindow = [[[UIApplication sharedApplication] delegate] window];
CGFloat bottom = mainWindow.safeAreaInsets.bottom;
make.bottom.equalTo(weakSelf.view).offset(bottom);
}else{
make.bottom.equalTo(weakSelf.view);
}
}];
修复方式2:修改上拉加载控件距离底部的高度 【推荐】
/** 忽略多少scrollView的contentInset的bottom */
//@property (assign, nonatomic) CGFloat ignoredScrollViewContentInsetBottom;
UIWindow *mainWindow = [[[UIApplication sharedApplication] delegate] window];
CGFloat bottom = mainWindow.safeAreaInsets.bottom;//34
_tableView.mj_footer.ignoredScrollViewContentInsetBottom = isIphoneX ? bottom : 0;
适配之后的效果
如果是第三方库建议升级最新版本,比如我把
MJRefresh
从3.1.16
升级至3.7.5
pod 'MJRefresh' , '3.7.5'
-> Installing MJRefresh 3.7.5 (was 3.1.16)