iOS13适配

作者: 二猪哥 | 来源:发表于2019-10-18 19:14 被阅读0次

    随着iPhone 11的发布,iOS 13适配也提上了日程,刚好最近在做项目适配,顺便总结一下:
    首先升级Xcode11,iOS13版本,因为Xcode11出现了一些新的API。废话不多说,直接上菜:

    一、私有KVC

    在iOS13中,不在允许通过KVC的方式去访问私有属性,需要通过其他方式去修改;之前如果使用到,会造成崩溃。

    在网上看到有人说 私有KVC崩溃与系统版本无关,与Xcode版本有关,Xcode11编译会崩溃。
    我个人觉得这个说法是错的;通过我的验证,我认为私有KVC在Xcode11---iOS12以下的系统不会崩溃,Xcode11---iOS13的会崩溃;大家也可以自己验证一下。

    目前我找到的会触发 KVC 访问权限异常崩溃的方法有:

    • UITabBarButton -> _info
    • UITextField -> _placeholderLabel
    • _UIBarBackground -> _shadowView
    • _UIBarBackground -> _backgroundEffectView
    • UISearchBar -> _cancelButtonText
    • UISearchBar -> _cancelButton
    • UISearchBar -> _searchField

    目前我项目中,主要用到UITextFieldUISearchBar的私有属性,现在我就拿这两个分析一下:

    1、UITextFiled 修改根据kvc提示文字的大小和颜色,

    这样写:

    [self.onePwdField setValue:color_cccccc forKeyPath:@"_placeholderLabel.textColor"];
    

    在Xcode11--iOS13会直接崩溃,修改方法为:

    self.onePwdField.attributedPlaceholder = [self placeholder:@"请输入原来的密码"];
    
    -(NSMutableAttributedString *)placeholder:(NSString *)text{
        if (text.length == 0) {
            return nil;
        }
        NSMutableAttributedString *att = [[NSMutableAttributedString alloc]initWithString:text attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:15],NSForegroundColorAttributeName:color_cccccc}];
        return att;
    }
    
    2、获取UISearchBar的textField

    在iOS13之前,我们通过"_searchField"来获取UISearchTextField来修改一些属性。

     UITextField *searchFiled = [self valueForKey:@"_searchField"];
    

    在iOS13中,继续这样会崩溃,修改方法为:

     UITextField *searchFiled;
     if(@available(iOS 13.0, *)) {
        searchFiled =  self.searchTextField; 
     }else{
        searchFiled = [self valueForKey:@"_searchField"];
     }
    
    3、UISearchBar 黑线处理导致崩溃

    iOS13之前为了处理搜索框的黑线问题,通常会遍历 searchBar 的 subViews,找到并删除 UISearchBarBackground
    在 iOS13 中这么做会导致 UI 渲染失败,然后直接崩溃,崩溃信息如下:

    Terminating app due to uncaught exception'NSInternalInconsistencyException', reason: 'Missing or detached view for search bar layout'
    

    修改方法为:设置 UISearchBarBackground 的 layer.contents 为 nil

     for (UIView *view in _searchBar.subviews.lastObject.subviews) {
       if ([view isKindOfClass:NSClassFromString(@"UISearchBarBackground")]) {
            view.layer.contents = nil;
            break;
        }
     } 
    
    4、 设置UISearchBar 的searchTextField.attributedPlaceholder无效问题。

    在 iOS13中需要把设置的代码写在viewDidAppear,亲测可以.

    5、获取状态栏视图,以下两种方法在iOS13导致崩溃。
    UIView *statusBar = [[UIApplication sharedApplication] valueForKey:@"statusBar"];
    或:
    UIView *statusBar = [[[UIApplication sharedApplication] valueForKey:@"statusBarWindow"] valueForKey:@"statusBar"];
    

    改为:

    if(@available(iOS 13.0, *)) {
            
    }else{
        UIView *statusBar = [[UIApplication sharedApplication] valueForKey:@"statusBar"];
        statusBar.transform = CGAffineTransformIdentity;
    }
    

    二、presentViewController的问题(模态弹出默认样式改变)

    在 iOS 13,使用 presentViewController 方式打开视图,会跟导航栏有部分视觉差,这里就不上图了,可以自行试一下。
    原因是:苹果将 UIViewControllermodalPresentationStyle 属性的默认值改成了新加的一个枚举值 UIModalPresentationAutomatic,对于多数 UIViewController,此值会映射成 UIModalPresentationPageSheet

    • 解决办法: 可以在vc present之前设置modalPresentationStyle 为 UIModalPresentationFullScreen

    • 另外,present的vc用户下拉可以dissmiss控制器,如果不想要这效果,可以这样设置

    /*当该属性为 false 时,用户下拉可以 dismiss 控制器,为 true 时,下拉不可以 dismiss控制器*/
    
    xxVC.isModalInPresentation = true;
    
    • 还有一点需要注意,原来以UIModalPresentationFullScreen样式弹出页面,那么这个页面弹出 ViewController 会依次调viewWillDisappear和 viewDidDisappear。然后在这个页面被 dismiss 的时候,将他弹出的那个 ViewController 的 viewWillAppear 和 viewDidAppear会被依次调用。然而使用默认的视差效果弹出页面,将他弹出的那个 ViewController 并不会调用这些方法,原先写在这四个函数中的代码以后都有可能会存在问题。

    三、蓝牙权限更新

    在 iOS 13 中,苹果将原来蓝牙申请权限用的 NSBluetoothPeripheralUsageDescription 字段,替换为 NSBluetoothAlwaysUsageDescription 字段。

    蓝牙权限.png

    四、废弃UIWebview 改用 WKWebView

    iOS13 开始苹果将 UIWebview 列为过期API(支持iOS2.0-iOS12)。 目前提交苹果应用市场(App Store)会发送邮件提示你在下一次提交时将应用中UIWebView 的 api 移除。

    暂时没有强制使用WKWebView,但是在iOS13开始UIWebView已是废弃的API,以后更高的版本中防止出现问题,尽早移除是上上之策。

    五、UISegmentedControl 默认样式改变。

    默认样式变为 白底黑字,如果设置修改过颜色的话,页面需要修改。

    六、WKWebView 中测量页面内容高度的方式变更

    iOS 13以前 document.body.scrollHeight iOS 13中 document.documentElement.scrollHeight 两者相差55 应该是浏览器定义高度变了。

    七、关于暗黑模式和切换

    1、暗黑模式是iOS13的一大亮点,下面来看看模式切换的设置
    (1) 切换 修改当前 UIViewController 或 UIView的模式。只要设置了控制器为暗黑模式,那么它子view也会对应的修改。

    即:只会影响当前的视图,不会影响前面的 controller 和后续 present 的 controller。

    if (@available(iOS 13.0, *)) {
       self.overrideUserInterfaceStyle =  UIUserInterfaceStyleDark;//UIUserInterfaceStyleLight
    } else {
            // Fallback on earlier versions
    }
    

    注意啦⚠️,但是当我们在 window 上设置 overrideUserInterfaceStyle 的时候,就会影响 window 下所有的 controller, view,包括后续推出的 controller。

    (2) 获取当前的模式
    if (@available(iOS 12.0, *)) {
         if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark ){
                // Dark
              NSLog(@"是dark模式、。。。");
         } else  if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleLight) {
                // Light
              NSLog(@"是light模式、。。。");
         } else {
                //unspecified
              NSLog(@"是unspecified模式、。。。");
         }
    }
    
    (3) 监听模式的变化
    - (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
        [super traitCollectionDidChange:previousTraitCollection];
    // trait发生了改变
        if (@available(iOS 13.0, *)) {
            if ([self.traitCollection hasDifferentColorAppearanceComparedToTraitCollection:previousTraitCollection]) {
                // 执行操作
            }
        } else {
            // Fallback on earlier versions
        }
    }
    
    2、适配暗黑模式
    (1)将同一个资源,创建出两种模式的样式。系统根据当前选择的样式,自动获取该样式的资源
    (2)每次系统更新样式时,应用会调用当前所有存在的元素调用对应的一些重新方法,进行重绘视图,可以在对应的方法做相应的改动。
    (3)资源文件适配
    • 1.创建一个Assets文件(或在现有的Assets文件中)
      详情请看:iOS中创建多个Assets.xcassets文件
    • 2.新建一个图片资源文件(或者颜色资源文件、或者其他资源文件)
    • 3.选中该资源文件, 打开 Xcode ->View ->Inspectors ->Show Attributes Inspectors (或者Option+Command+4)视图,将Apperances 选项 改为Any,Dark
    • 4.执行完第三步,资源文件将会有多个容器框,分别为 Any Apperance 和 Dark Apperance. Any Apperance 应用于默认情况(Unspecified)与高亮情况(Light), Dark Apperance 应用于暗黑模式(Dark)
    • 5.代码默认执行时,就可以正常通过名字使用了,系统会根据当前模式自动获取对应的资源文件

    注意啦⚠️,同一工程内多个Assets文件在打包后,就会生成一个Assets.car 文件,所以要保证Assets内资源文件的名字不能相同.

    • 步骤如图: 资源文件适配暗黑模式.png
    资源文件适配暗黑模式.png
    3、全局关闭黑暗模式
    • 方式一 配置plist文件: 在Info.plist 文件中,添加UIUserInterfaceStyle key 名字为 User Interface Style 值为String,将UIUserInterfaceStyle key 的值设置为 Light。


      全局关闭黑暗模式.png

    在开发中,如果用的系统控件(如cell、tableview的背景色)未设置背景色(或者为透明),则进入暗黑模式后,控件背景色变为黑色。

    可以每一个页面设置,当然也可以整体设置, 一般我们的APP都是在一个window下的,那就整体设置APP里的window

    • 方式二 :代码关闭黑暗模式 强制关闭暗黑模式
    #if defined(__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
    if(@available(iOS 13.0,*)){
        self.window.overrideUserInterfaceStyle = UIUserInterfaceStyleLight;
    }
    #endif
    
    4、单个界面不遵循暗黑模式
    • UIViewController与UIView 都新增一个属性 overrideUserInterfaceStyle
    • 将 overrideUserInterfaceStyle 设置为对应的模式,则强制限制该元素与其子元素以设置的模式进行展示,不跟随系统模式改变进行改变

    1、设置 ViewController 的该属性, 将会影响视图控制器的视图和子视图控制器采用该样式。
    2、设置 View 的该属性, 将会影响视图及其所有子视图采用该样式。
    3、设置 Window 的该属性, 将会影响窗口中的所有内容都采用样式,包括根视图控制器和在该窗口中显示内容的所有演示控制器(UIPresentationController)

    更详细的暗黑模式设置请看:iOS适配暗黑模式

    八、即将废弃的 LaunchImage(iOS 7.0–13.0)

    从 iOS 8 的时候,苹果就引入了 LaunchScreen.storyboard,我们可以设置 LaunchScreen来作为启动页。当然,现在你还可以使用LaunchImage来设置启动图。不过使用LaunchImage的话,要求我们必须提供各种屏幕尺寸的启动图,来适配各种设备,随着苹果设备尺寸越来越多,这种方式显然不够 Flexible。而使用 LaunchScreen的话,情况会变的很简单, LaunchScreen是支持AutoLayout+SizeClass的,所以适配各种屏幕都不在话下。

    注意啦⚠️,从2020年4月开始,所有使⽤ iOS13 SDK 的 App 将必须提供 LaunchScreen.storyboard,LaunchImage即将退出历史舞台,否则将无法提交到 App Store 进行审批。

    九、StatusBar 与之前版本不同

    目前状态栏也增加了一种模式,由之前的两种,变成了三种, 其中default由之前的黑色内容,变成了会根据系统模式,自动选择当前展示lightContent还是darkContent。

    十、iOS13 获取window适配

        UIWindow* window = nil;
         
        if (@available(iOS 13.0, *)) {
            for (UIWindowScene* windowScene in [UIApplication sharedApplication].connectedScenes)
            {
               if (windowScene.activationState == UISceneActivationStateForegroundActive)
               {
                    window = windowScene.windows.firstObject;
         
                    break;
               }
            }
        }else{
            window = [UIApplication sharedApplication].keyWindow;
        }
    

    相关文章

      网友评论

        本文标题:iOS13适配

        本文链接:https://www.haomeiwen.com/subject/ypoimctx.html