iOS 13适配填坑总结

前言

  前段时间苹果发布了最新系统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;
 }

打断点发现:

image.png

keywindownextResponder居然在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居然不在主线程了,崩溃的原因也是因此。

image.png

image.png

临时解决方法是加一个安全线程的调用:

#define dispatch_main_async_safe(block)\
    if ([NSThread isMainThread]) {\
        block();\
    } else {\
        dispatch_async(dispatch_get_main_queue(), block);\
    }

首先声明这个方法只是临时阻止UIWebViewcrash的一种临时方法,本人并不建议这么做,更好的的方法是更换WKWebView,因为UIWebView在iOS 12就已经被废弃。

image.png

3、暗黑模式(Dark Model)

两种方法:
1)直接全局关闭暗黑模式(不建议)
info.plist中加入:key 为User Interface Style value类型为String值为Light可直接全局禁用暗黑模式
2)根据风格适配
我借鉴了这篇文章

4、UIViewController的UIModalPresentationStyle属性

在iOS 13未适配的时候突然发现present出来的控制器样式变了,在查看了相关属性的情况之后发现了UIModalPresentationStyle新增了一个枚举值UIModalPresentationAutomatic在iOS 13之后控制的默认值就是这个,要想恢复之前的模态跳转样式需要设置控制器UIModalPresentationStyleUIModalPresentationFullScreen就好:

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]}];
待续。。。

你可能感兴趣的:(iOS 13适配填坑总结)