美文网首页
iOS 新机适配原理及更新三方应用——让 TIM 适配 Xs M

iOS 新机适配原理及更新三方应用——让 TIM 适配 Xs M

作者: Magic_Unique | 来源:发表于2019-01-27 00:41 被阅读0次

    背景

    自从买了 iPhone Xs Max,就一直处于等待应用适配新设备的状态。不过本次增加的新屏幕(Xs Max)与 iPhone X 的屏幕比例相似,所以即使应用没有第一时间适配 Xs Max 机型,iOS 也会把应用的窗口以 iPhone X 为基础拉伸到满屏,以便最大限度的展示应用界面。相比之前推出 X 和 Plus 系列时,用户的过度成本降低了许多。

    但是在苦苦等待过程中,腾讯的 TIM 一直没有得到更新。即便是等待苹果开售 iPhone Xr(Xr 在 Xs 开售后一个月才开售,所以开发者可能会等到 Xr 开售后才一起适配 Xs Max 和 Xr),还是等待苹果修复新的 Assets 压缩算法在 iOS 9 下闪退的 bug,微信也在年底前完成了 Xs Max 的适配,并且推出了微信 7.0。然而直到作者编写此文(2019年01月26日)时,TIM 依旧没有更新,粗糙的 UI 元素让人觉得不是一个 1 万块钱手机该有的界面。处于强迫症考虑,能否在没有三方应用源码的情况下让三方应用适配新的机型呢?

    本文将通过以下几点的研究,来学习苹果的应用适配之道。

    • 在发布新设备之后,苹果如何让没有适配新设备的应用正常运行在新的设备上
    • 在发布新设备之后,开发者如何让自己的应用适配新的设备
    • 在发布新设备之后,如何让三方应用适配新的设备(以 TIM 为例)

    另外简单介绍一下 TIM:

    TIM 是腾讯推出的一款精简版的 QQ。腾讯已推出多个版本的QQ,如:QQ、国际版、HD 版、TIM。TIM 是一个面向工作、白领的一款产品。拥有强大的文件管理,以及精简的 UI 界面。由于他使用简单,办公高效,得到极多喜欢简约风格的用户(包括作者)一致好评。

    PS:本文只探讨 iPhone,不探讨 iPad。

    iPad 原理如同 iPhone。

    iOS 如何兼容没有适配新屏幕的应用

    即使苹果在开发者协议中注明:任何时候,开发者上传到 App Store 的应用必须适配最新的设备,否则被拒(你爸爸还是你爸爸)。苹果也无法保证在开售新设备的时候,已经在 App Store 上的应用全部都已被更新过。所以每当新设备(新屏幕)发布时,苹果都要在新的 iOS 系统中让新的设备兼容旧的应用。

    先回忆一下苹果发布的几款机型(主要探讨屏幕),以及用户过渡期的处理方式(兼容已经在 App Store 上还未适配新设备的应用)。

    从 2007 年第一代 iPhone 发布一直到 2018 年的 iPhone Xs Max,苹果一共发布了 8 款 iPhone 屏幕,它们分别如下(以搭载该屏幕的第一款设备命名,以发布时间及市场定位排序):

    Name Pixel Size PPI Scale 过渡期的自适配方式
    iPhone 320 x 480 320 x 480 326 1 iPhone 祖宗,无需适配
    iPhone 4 640 x 960 320 x 480 326 2 宽度拉伸,高度拉伸,无黑边
    iPhone 5 640 x 1136 320 x 568 326 2 宽度不变,垂直居中,上下黑边
    iPhone 6 750 x 1334 375 x 667 326 2 宽度拉伸,垂直居中,上下黑边
    iPhone 6 Plus 1242 x 2208 414 x 736 401 3 宽度拉伸,垂直居中,上下黑边
    iPhone X 1125 x 2436 375 x 812 458 3 宽度拉伸,垂直居中,上下黑边
    iPhone Xr 1242 x 2688 414 x 896 326 2 宽度拉伸,高度拉伸,无黑边
    iPhone Xs Max 828 x 1792 414 x 896 458 3 宽度拉伸,高度拉伸,无黑边

    如果新的设备的长宽比和旧的设备相同(Size 相同,或者 Size.width / Size.height 近似相同),如:iPhone 到 iPhone 4,iPhone X 到 iPhone Xs Max,就单纯的按照旧的设备来渲染(UIScreen.mainScreen.bounds 还是旧的设备的值),并且等比例拉伸到全屏,所以会变得有的线条有点粗犷 = =。

    如果新的设备的长款比和旧的设备不同,如:iPhone 4 到 iPhone 5(脸被拉长了),iPhone 5 到 iPhone 6 和 Plus(不仅脸长了,还胖了)。就以宽度为基准尽可能的等比例放大,长度不够的黑边凑,同时整体垂直居中(这样就导致上下留有黑边)。

    以上两种处理虽然最终都不能完美解决问题,但是至少保证旧的应用在渲染 UI 的时候不会因屏幕分辨率而导致布局错乱。

    为什么有 Autolayout 还不能完美自适应呢?

    因为你 Autolayout 再怎么牛逼,也躲不过无刘海到有刘海的变化。。。

    应用开发者如何适配新的屏幕

    对于使用 UIKit.framework 提供的几个常用接口来开发 UI 的应用,在适配新屏幕时就非常的简便,一般只有以下三步骤:

    1. 下载新的 Xcode
    2. 加入新屏幕对应大小的启动图
    3. 重新构建应用

    对于大量自定义 UI 框架的应用(如自己重写了 UINavigationController),需要在代码中加入新设备的判断(此文不考虑这种情况)。

    在这里必须吐槽一下,每当有新屏幕出现,网络上就有大量的博客:《教你如何适配新的 iPhone》,《适配新的 iPhone 看我就够了》,《你真的适配新的 iPhone 了吗》。。。
    甚至有的公司为了适配新的 iPhone 花了至少一个工作日的时间。

    如果应用使用苹果标准 API 构建界面,真的只需要添加一个启动图然后重新打包即可解决问题。

    我们对每一个步骤进行一次分析:

    1. 使用最新的 Xcode

    使用最新的 Xcode 无疑是正确的。最新的 Xcode 可以给我们带来两个功能:

    • 新的 iOS SDK
    • 新屏幕的启动图坑位

    这两个功能在适配新的设备时给了很大的帮助。新的 SDK 能够让应用在新的 iOS 系统上完美运行。新的启动图能够让应用获取到正确的屏幕大小。

    2. 加入屏幕对应大小的启动图

    如果某个应用使用纯代码开发(没有使用 Storyboardxib),那么这个应用的启动代码应该如下:

    - (BOOL)application:(UIApplication *)application didFinishLaunchedWithOptions:(NSDictionary *)launchOptions {
        self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
        self.window.rootViewController = [[ViewController alloc] init];
        [self.window makeKeyAndVisible];
    }
    

    适配新的屏幕,本质就是这个 self.window 对象的大小等于新的屏幕,然后子元素根据新的窗口大小来创建布局约束。

    代码中动态使用 [UIScreen mainScreen].bounds 的大小来创建 window 对象,似乎是完美适配了任何大小的屏幕。但实际上,如果没有添加新的设备的启动图,UIScreen 获取到的屏幕大小并非真实设备的屏幕大小,而是 iOS 兼容模式所用的旧设备的屏幕大小。这是 iOS 兼容模式的一种优化,防止新的屏幕分辨率的出现导致应用 UI 控件布局错乱(宁愿等比例拉伸)。

    3. 重新构建应用

    不用多说了。

    猜测——判断应用是否需要在兼容模式

    首先有一个客观事实:

    当新的设备发布后,如果使用旧的 Xcode 打包的 ipa 一样无法适配新机
    如果使用新的 Xcode 并且没有加入新的启动图的 ipa 一样无法适配新机。

    因此是否适配取决于 Xcode 版本号以及是否包含启动图,与打包的时间无关。

    之前说明,开发者适配新设备的方法是:使用最新的 Xcode,加入新的设备的启动图后重新打包应用。所以 iOS 只需判断这个应用是否经过这样方式处理,就可以知道这个应用是否需要兼容模式下运行。

    1. 判断是否包含启动图

    判断是否是启动图是一件很简单的事情,只需要判断当前屏幕分辨率对应的图片是否存在在 .app 文件夹中即可,这个判断会经历从 Info.plist 读取启动图配置、查找启动图的几个阶段,此文不在阐述。

    总之肯定会多一个 LaunchImage-xxx-xxx@nx.png 图片文件,并且分辨率和别的启动图不同。

    2. 判断是否是新的 Xcode 打包

    如何判断这个 ipa 是用什么版本的 Xcode 打的包呢?

    在一个 ipa 包中有几个特殊文件:

    • Binary
    • Info.plist
    • _CodeSignature

    clang 中不包含有 Xcode 版本信息,同时在编译的脚本中也没有将 Xcode 的版本信息作为参数传递给 clang,所以二进制(Binary)文件中不含有有关 Xcode 信息的数据。

    _CodeSignature 是签名产物,签名时也没有 Xcode 信息,所以也不可能有 Xcode 信息的数据。

    Info.plist 是 App 的 Manifest 文件,包含 App 的各种信息。这是我们着重考虑的方向。

    分析 Info.plist 文件

    打开一个自己做的 app 的 Info.plist 文件,可以查看到一下内容:

    Info.plist

    其中红色标记的就是作者查找出来带有编译环境的信息:

    • DTSDKName 开发所用的 SDK 名称
    • DTXcode 开发所用的 Xcode 版本号(Xcode 10.1.0)
    • DTSDKBuild 开发所用的 SDK Build 号(SDK 内部版本号)
    • BuildMachineOSBuild 开发所用的机器 Build 号(Mac 的内部版本号)
    • DTPlatformVersion 开发所用的平台版本号
    • DTXcodeBuild 开发所用的 Xcode Build 号(Xcode 内部版本号)
    • DTPlatformBuild 开发所用的平台 Build 号(平台的内部版本号)

    已经很完美了,Xcode、MacOS、SDK 版本号全都在。

    同时我们也可以看见,如果应用使用的是启动图,而不是 Storyboard,在 Info.plist 文件中会包含一项 UILaunchImages:

    使用启动图的 Info.plist

    由此我们可以猜测苹果的兼容方案。

    苹果的兼容方案

    当 iOS 要启动一个 App 时,会先读取 Info.plist 对应编译环境版本号,判断是否是新版 Xcode 打的包。如果是新版本的 Xcode,再判断是否包含启动图。如果包含启动图,则说明该 ipa 适配了新机,无需兼容。反之,使用兼容模式运行此 app。

    iOS 兼容模式

    验证——将三方应用适配新机(以 TIM 为例)

    鉴于以上的分析,只需要将 Info.plist 中的对应版本号提高,并且加入新的启动图即可实现三方应用适配新机。

    1. 砸壳

    由于所有在 App Store 上的应用均被苹果加壳,并且我们将来需要修改 Info.plist 文件,所以必须要重签名。因此我们所修改的 TIM 需要砸壳处理。

    砸壳方式有很多种,此文不阐述。

    参考:Clutch、frida-ios-dump 等等。

    2. 修改 Info.plist 中的版本号

    由于 Info.plist 中版本号有点复杂,有部分为内部版本号(需要查询),所以我将已经适配好 Xs Max 的应用拿来做参考,进行数据覆盖。

    此处使用的是 WeChat 7.0.0 的 Info.plist 作为参考依据。将上述提到的 7 个字段的值,从微信的 Info.plist 文件里提取出来,覆盖到 TIM 的 Info.plist 文件中。分别是:DTSDKNameDTXcodeDTSDKBuildBuildMachineOSBuildDTPlatformVersionDTXcodeBuildDTPlatformBuild

    除版本号外,Info.plist 也包含支持设备的硬件号,字段为 UISupportedDevices

    通过助手类软件可以读出 Xs Max 设备号为 iPhone11,6,将其加入到 UISupportedDevices 字段中,防止安装时报错(不支持此设备)

    3. 添加启动图

    经过与微信的比较,发现 Xs Max 和 Xr 的启动图分别是:

    名称 大小 机型
    LaunchImage-1200-Portrait-1792h@2x.png 828 x 1792 Xr
    LaunchImage-1200-Portrait-2688h@3x.png 1242 x 2688 Xs Max

    终于祭出了自己的美工技术了。提取 X 的启动图,修改为上述两个文件,并添加到包中。

    打开 Info.plist 文件,比较后在 UILaunchImages 的数组里添加两项启动图配置:

    <dict>
        <key>UILaunchImageOrientation</key>
        <string>Portrait</string>
        <key>UILaunchImageName</key>
        <string>LaunchImage-1200-Portrait-2688h</string>
        <key>UILaunchImageSize</key>
        <string>{414, 896}</string>
        <key>UILaunchImageMinimumOSVersion</key>
        <string>12.0</string>
    </dict>
    <dict>
        <key>UILaunchImageOrientation</key>
        <string>Portrait</string>
        <key>UILaunchImageName</key>
        <string>LaunchImage-1200-Portrait-1792h</string>
        <key>UILaunchImageSize</key>
        <string>{414, 896}</string>
        <key>UILaunchImageMinimumOSVersion</key>
        <string>12.0</string>
    </dict>
    

    保存。

    4. 重签名

    重签名的方式有很多种,此文不再详述。

    至于重签名后无法正常推送,不在此文的探讨范围,我就不详细说明了。

    对比图

    左侧为适配前,右侧为适配后

    聊天列表 文件管理 好友列表

    适配后的 UI 变得非常的细腻。舒服~~

    总结

    Xcode 在打包的时候讲打包环境信息(各种版本号)保存到 Info.plist 中。iOS 运行应用时,通过 Info.plist 中的版本号进行比较,来判断开发者是否适配了新设备,从而判断是否需要兼容模式运行此应用。

    相关文章

      网友评论

          本文标题:iOS 新机适配原理及更新三方应用——让 TIM 适配 Xs M

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