前言
前段时间苹果发布了最新系统iOS 13,开发者当然避免不了去适配最新的系统版本,以下我总结了在适配过程中遇到的问题以及坑。
1、获取当前控制器
我在适配iOS 13之前获取当前控制器的方法是这样的
- (UIViewController *)currentViewController {
UIViewController *result = nil;
UIWindow *keyWindow = [UIApplication sharedApplication].delegate.window;
if (keyWindow.windowLevel != UIWindowLevelNormal) {
NSArray *windows = [UIApplication sharedApplication].windows;
for (UIWindow *tempWindow in windows) {
if (tempWindow.windowLevel == UIWindowLevelNormal) {
keyWindow = tempWindow;
break;
}
}
}
id nextResponder = [[keyWindow.subviews firstObject] nextResponder];
if ([nextResponder isKindOfClass:[UITabBarController class]]) {
UITabBarController *tabBarController = (UITabBarController*)nextResponder;
result = tabBarController.selectedViewController;
if ([result isKindOfClass:[UINavigationController class]]) {
UINavigationController *naviC = (UINavigationController*)result;
result = naviC.visibleViewController;
}
} else if ([nextResponder isKindOfClass:[UINavigationController class]]) {
UINavigationController *naviC = (UINavigationController*)nextResponder;
result = naviC.visibleViewController;
} else if ([nextResponder isKindOfClass:[UIViewController class]]) {
result = (UIViewController*)nextResponder;
} else {
result = keyWindow.rootViewController;
}
return result;
}
打断点发现:
keywindow
的nextResponder
居然在iOS 13 中居然也变成了UIWindow
。
所以我们可以通过绕过
nextResponder
来获取当前控制器:
-(UIViewController *)currentViewController{
UIWindow *window = [UIApplication sharedApplication].delegate.window;
NSLog(@"window level: %.0f", window.windowLevel);
if (window.windowLevel != UIWindowLevelNormal) {
NSArray *windows = [[UIApplication sharedApplication] windows];
for (UIWindow * tmpWin in windows) {
if (tmpWin.windowLevel == UIWindowLevelNormal) {
window = tmpWin;
break;
}
}
}
//从根控制器开始查找
UIViewController *rootVC = window.rootViewController;
UIViewController *activityVC = nil;
while (true) {
if ([rootVC isKindOfClass:[UINavigationController class]]) {
activityVC = [(UINavigationController *)rootVC visibleViewController];
} else if ([rootVC isKindOfClass:[UITabBarController class]]) {
activityVC = [(UITabBarController *)rootVC selectedViewController];
} else if (rootVC.presentedViewController) {
activityVC = rootVC.presentedViewController;
}else {
break;
}
rootVC = activityVC;
}
return activityVC;
}
解决。
2、UIWebView的执行JS脚本方法回调线程问题
在iOS 13之前中用JSContext
执行脚本方法回调的block
是在主线程的,可以之间在里面修改UI布局,但是在iOS 13中突然就崩溃了,我在调试中发现了这个回调block
居然不在主线程了,崩溃的原因也是因此。
临时解决方法是加一个安全线程的调用:
#define dispatch_main_async_safe(block)\
if ([NSThread isMainThread]) {\
block();\
} else {\
dispatch_async(dispatch_get_main_queue(), block);\
}
首先声明这个方法只是临时阻止UIWebView
crash的一种临时方法,本人并不建议这么做,更好的的方法是更换WKWebView
,因为UIWebView在iOS 12就已经被废弃。
3、暗黑模式(Dark Model)
两种方法:
1)直接全局关闭暗黑模式(不建议)
在info.plist
中加入:key 为User Interface Style
value类型为String值为Light
可直接全局禁用暗黑模式
2)根据风格适配
我借鉴了这篇文章
4、UIViewController的UIModalPresentationStyle
属性
在iOS 13未适配的时候突然发现present出来的控制器样式变了,在查看了相关属性的情况之后发现了UIModalPresentationStyle
新增了一个枚举值UIModalPresentationAutomatic
在iOS 13之后控制的默认值就是这个,要想恢复之前的模态跳转样式需要设置控制器UIModalPresentationStyle
为UIModalPresentationFullScreen
就好:
if (@available(iOS 13.0, *)) {//iOS 13 默认不使用 UIModalPresentationFullScreen 模式
self.modalPresentationStyle = UIModalPresentationFullScreen;
}
5、UITextField的leftview
在iOS 13中突然发现我的UITextfield leftview显示有点怪,然后发现如果直接给 textfield.leftView 赋值一个 UILabel 对象,他的宽高会被 sizeToFit。解决方法:给label套一层UIView的父视图在设置leftview。解决
6、Push推送获取的token的变化
在iOS 13的系统版本中,token的格式发生了变得,导致不能按照iOS13之前的处理方法去处理然后再上传服务器。
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
DBLOG_FUN;
NSString *_deviceToken = @"";
if (@available(iOS 13.0, *)) {
if (![deviceToken isKindOfClass:[NSData class]]) return;
const unsigned *tokenBytes = [deviceToken bytes];
_deviceToken = [NSString stringWithFormat:@"%08x%08x%08x%08x%08x%08x%08x%08x",
ntohl(tokenBytes[0]), ntohl(tokenBytes[1]), ntohl(tokenBytes[2]),
ntohl(tokenBytes[3]), ntohl(tokenBytes[4]), ntohl(tokenBytes[5]),
ntohl(tokenBytes[6]), ntohl(tokenBytes[7])];
[[DataStore sharedStore] setDeviceToken:_deviceToken];
DBLOG(@"_deviceToken:%@", _deviceToken);
}else if([deviceToken.description length] > 0) {
NSCharacterSet *set = [NSCharacterSet characterSetWithCharactersInString:@"<>"];
_deviceToken = [deviceToken.description stringByTrimmingCharactersInSet:set];
_deviceToken = [_deviceToken stringByReplacingOccurrencesOfString:@" " withString:@""];
[[DataStore sharedStore] setDeviceToken:_deviceToken];
DBLOG(@"_deviceToken:%@", _deviceToken);
}
}
7、UITextfield通过KVC修改placeholderlabel方法
在iOS 13中通过以下方法修改UITextfield的placeholder会发生crash:
// 使用的私有方法
[_textField setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];
解决方法有两种:
1)把前面的下划线去掉(不建议)
[_textField setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];
2)通过attributedPlaceholder
设置(建议)
_textField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:@"输入"attributes:@{NSForegroundColorAttributeName: [UIColor redColor]}];