背景
自從買了 iPhone Xs Max仔掸,就一直處于等待應(yīng)用適配新設(shè)備的狀態(tài)脆贵。不過本次增加的新屏幕(Xs Max)與 iPhone X 的屏幕比例相似,所以即使應(yīng)用沒有第一時間適配 Xs Max 機(jī)型起暮,iOS 也會把應(yīng)用的窗口以 iPhone X 為基礎(chǔ)拉伸到滿屏丹禀,以便最大限度的展示應(yīng)用界面。相比之前推出 X 和 Plus 系列時鞋怀,用戶的過度成本降低了許多双泪。
但是在苦苦等待過程中,騰訊的 TIM 一直沒有得到更新密似。即便是等待蘋果開售 iPhone Xr(Xr 在 Xs 開售后一個月才開售焙矛,所以開發(fā)者可能會等到 Xr 開售后才一起適配 Xs Max 和 Xr),還是等待蘋果修復(fù)新的 Assets 壓縮算法在 iOS 9 下閃退的 bug残腌,微信也在年底前完成了 Xs Max 的適配村斟,并且推出了微信 7.0。然而直到作者編寫此文(2019年01月26日)時抛猫,TIM 依舊沒有更新蟆盹,粗糙的 UI 元素讓人覺得不是一個 1 萬塊錢手機(jī)該有的界面。處于強(qiáng)迫癥考慮闺金,能否在沒有三方應(yīng)用源碼的情況下讓三方應(yīng)用適配新的機(jī)型呢逾滥?
本文將通過以下幾點的研究,來學(xué)習(xí)蘋果的應(yīng)用適配之道败匹。
- 在發(fā)布新設(shè)備之后寨昙,蘋果如何讓沒有適配新設(shè)備的應(yīng)用正常運(yùn)行在新的設(shè)備上
- 在發(fā)布新設(shè)備之后讥巡,開發(fā)者如何讓自己的應(yīng)用適配新的設(shè)備
- 在發(fā)布新設(shè)備之后,如何讓三方應(yīng)用適配新的設(shè)備(以 TIM 為例)
另外簡單介紹一下 TIM:
TIM 是騰訊推出的一款精簡版的 QQ舔哪。騰訊已推出多個版本的QQ欢顷,如:QQ、國際版捉蚤、HD 版抬驴、TIM。TIM 是一個面向工作缆巧、白領(lǐng)的一款產(chǎn)品布持。擁有強(qiáng)大的文件管理,以及精簡的 UI 界面盅蝗。由于他使用簡單鳖链,辦公高效姆蘸,得到極多喜歡簡約風(fēng)格的用戶(包括作者)一致好評墩莫。
PS:本文只探討 iPhone,不探討 iPad逞敷。
iPad 原理如同 iPhone狂秦。
iOS 如何兼容沒有適配新屏幕的應(yīng)用
即使蘋果在開發(fā)者協(xié)議中注明:任何時候,開發(fā)者上傳到 App Store 的應(yīng)用必須適配最新的設(shè)備推捐,否則被拒(你爸爸還是你爸爸)裂问。蘋果也無法保證在開售新設(shè)備的時候,已經(jīng)在 App Store 上的應(yīng)用全部都已被更新過牛柒。所以每當(dāng)新設(shè)備(新屏幕)發(fā)布時堪簿,蘋果都要在新的 iOS 系統(tǒng)中讓新的設(shè)備兼容舊的應(yīng)用。
先回憶一下蘋果發(fā)布的幾款機(jī)型(主要探討屏幕)皮壁,以及用戶過渡期的處理方式(兼容已經(jīng)在 App Store 上還未適配新設(shè)備的應(yīng)用)椭更。
從 2007 年第一代 iPhone 發(fā)布一直到 2018 年的 iPhone Xs Max,蘋果一共發(fā)布了 8 款 iPhone 屏幕蛾魄,它們分別如下(以搭載該屏幕的第一款設(shè)備命名虑瀑,以發(fā)布時間及市場定位排序):
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 | 寬度拉伸,高度拉伸谈截,無黑邊 |
如果新的設(shè)備的長寬比和舊的設(shè)備相同(Size 相同筷屡,或者 Size.width / Size.height 近似相同),如:iPhone 到 iPhone 4簸喂,iPhone X 到 iPhone Xs Max毙死,就單純的按照舊的設(shè)備來渲染(UIScreen.mainScreen.bounds
還是舊的設(shè)備的值),并且等比例拉伸到全屏喻鳄,所以會變得有的線條有點粗獷 = =扼倘。
如果新的設(shè)備的長款比和舊的設(shè)備不同,如:iPhone 4 到 iPhone 5(臉被拉長了)除呵,iPhone 5 到 iPhone 6 和 Plus(不僅臉長了再菊,還胖了)。就以寬度為基準(zhǔn)盡可能的等比例放大颜曾,長度不夠的黑邊湊纠拔,同時整體垂直居中(這樣就導(dǎo)致上下留有黑邊)。
以上兩種處理雖然最終都不能完美解決問題泛豪,但是至少保證舊的應(yīng)用在渲染 UI 的時候不會因屏幕分辨率而導(dǎo)致布局錯亂稠诲。
為什么有 Autolayout 還不能完美自適應(yīng)呢?
因為你 Autolayout 再怎么牛逼诡曙,也躲不過無劉海到有劉海的變化臀叙。。价卤。
應(yīng)用開發(fā)者如何適配新的屏幕
對于使用 UIKit.framework 提供的幾個常用接口來開發(fā) UI 的應(yīng)用劝萤,在適配新屏幕時就非常的簡便,一般只有以下三步驟:
- 下載新的 Xcode
- 加入新屏幕對應(yīng)大小的啟動圖
- 重新構(gòu)建應(yīng)用
對于大量自定義 UI 框架的應(yīng)用(如自己重寫了 UINavigationController
)荠雕,需要在代碼中加入新設(shè)備的判斷(此文不考慮這種情況)稳其。
在這里必須吐槽一下,每當(dāng)有新屏幕出現(xiàn)炸卑,網(wǎng)絡(luò)上就有大量的博客:《教你如何適配新的 iPhone》既鞠,《適配新的 iPhone 看我就夠了》,《你真的適配新的 iPhone 了嗎》盖文。嘱蛋。。
甚至有的公司為了適配新的 iPhone 花了至少一個工作日的時間。如果應(yīng)用使用蘋果標(biāo)準(zhǔn) API 構(gòu)建界面洒敏,真的只需要添加一個啟動圖然后重新打包即可解決問題龄恋。
我們對每一個步驟進(jìn)行一次分析:
1. 使用最新的 Xcode
使用最新的 Xcode 無疑是正確的。最新的 Xcode 可以給我們帶來兩個功能:
- 新的 iOS SDK
- 新屏幕的啟動圖坑位
這兩個功能在適配新的設(shè)備時給了很大的幫助凶伙。新的 SDK 能夠讓應(yīng)用在新的 iOS 系統(tǒng)上完美運(yùn)行郭毕。新的啟動圖能夠讓應(yīng)用獲取到正確的屏幕大小。
2. 加入屏幕對應(yīng)大小的啟動圖
如果某個應(yīng)用使用純代碼開發(fā)(沒有使用 Storyboard
和 xib
)函荣,那么這個應(yīng)用的啟動代碼應(yīng)該如下:
- (BOOL)application:(UIApplication *)application didFinishLaunchedWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.rootViewController = [[ViewController alloc] init];
[self.window makeKeyAndVisible];
}
適配新的屏幕显押,本質(zhì)就是這個 self.window
對象的大小等于新的屏幕,然后子元素根據(jù)新的窗口大小來創(chuàng)建布局約束傻挂。
代碼中動態(tài)使用 [UIScreen mainScreen].bounds
的大小來創(chuàng)建 window
對象乘碑,似乎是完美適配了任何大小的屏幕。但實際上金拒,如果沒有添加新的設(shè)備的啟動圖兽肤,UIScreen
獲取到的屏幕大小并非真實設(shè)備的屏幕大小,而是 iOS 兼容模式所用的舊設(shè)備的屏幕大小绪抛。這是 iOS 兼容模式的一種優(yōu)化资铡,防止新的屏幕分辨率的出現(xiàn)導(dǎo)致應(yīng)用 UI 控件布局錯亂(寧愿等比例拉伸)。
3. 重新構(gòu)建應(yīng)用
不用多說了睦疫。
猜測——判斷應(yīng)用是否需要在兼容模式
首先有一個客觀事實:
當(dāng)新的設(shè)備發(fā)布后害驹,如果使用舊的 Xcode 打包的 ipa 一樣無法適配新機(jī)
如果使用新的 Xcode 并且沒有加入新的啟動圖的 ipa 一樣無法適配新機(jī)鞭呕。因此是否適配取決于 Xcode 版本號以及是否包含啟動圖蛤育,與打包的時間無關(guān)。
之前說明葫松,開發(fā)者適配新設(shè)備的方法是:使用最新的 Xcode瓦糕,加入新的設(shè)備的啟動圖后重新打包應(yīng)用。所以 iOS 只需判斷這個應(yīng)用是否經(jīng)過這樣方式處理腋么,就可以知道這個應(yīng)用是否需要兼容模式下運(yùn)行咕娄。
1. 判斷是否包含啟動圖
判斷是否是啟動圖是一件很簡單的事情,只需要判斷當(dāng)前屏幕分辨率對應(yīng)的圖片是否存在在 .app 文件夾中即可珊擂,這個判斷會經(jīng)歷從 Info.plist 讀取啟動圖配置圣勒、查找啟動圖的幾個階段,此文不在闡述摧扇。
總之肯定會多一個 LaunchImage-xxx-xxx@nx.png 圖片文件圣贸,并且分辨率和別的啟動圖不同。
2. 判斷是否是新的 Xcode 打包
如何判斷這個 ipa 是用什么版本的 Xcode 打的包呢扛稽?
在一個 ipa 包中有幾個特殊文件:
- Binary
- Info.plist
- _CodeSignature
clang 中不包含有 Xcode 版本信息吁峻,同時在編譯的腳本中也沒有將 Xcode 的版本信息作為參數(shù)傳遞給 clang,所以二進(jìn)制(Binary)文件中不含有有關(guān) Xcode 信息的數(shù)據(jù)。
_CodeSignature 是簽名產(chǎn)物用含,簽名時也沒有 Xcode 信息矮慕,所以也不可能有 Xcode 信息的數(shù)據(jù)。
Info.plist 是 App 的 Manifest 文件啄骇,包含 App 的各種信息痴鳄。這是我們著重考慮的方向。
分析 Info.plist 文件
打開一個自己做的 app 的 Info.plist 文件缸夹,可以查看到一下內(nèi)容:
其中紅色標(biāo)記的就是作者查找出來帶有編譯環(huán)境的信息:
- DTSDKName 開發(fā)所用的 SDK 名稱
- DTXcode 開發(fā)所用的 Xcode 版本號(Xcode 10.1.0)
- DTSDKBuild 開發(fā)所用的 SDK Build 號(SDK 內(nèi)部版本號)
- BuildMachineOSBuild 開發(fā)所用的機(jī)器 Build 號(Mac 的內(nèi)部版本號)
- DTPlatformVersion 開發(fā)所用的平臺版本號
- DTXcodeBuild 開發(fā)所用的 Xcode Build 號(Xcode 內(nèi)部版本號)
- DTPlatformBuild 開發(fā)所用的平臺 Build 號(平臺的內(nèi)部版本號)
已經(jīng)很完美了夏跷,Xcode、MacOS明未、SDK 版本號全都在槽华。
同時我們也可以看見,如果應(yīng)用使用的是啟動圖趟妥,而不是 Storyboard
猫态,在 Info.plist 文件中會包含一項 UILaunchImages
:
由此我們可以猜測蘋果的兼容方案。
蘋果的兼容方案
當(dāng) iOS 要啟動一個 App 時披摄,會先讀取 Info.plist 對應(yīng)編譯環(huán)境版本號亲雪,判斷是否是新版 Xcode 打的包。如果是新版本的 Xcode疚膊,再判斷是否包含啟動圖义辕。如果包含啟動圖,則說明該 ipa 適配了新機(jī)寓盗,無需兼容灌砖。反之,使用兼容模式運(yùn)行此 app傀蚌。
驗證——將三方應(yīng)用適配新機(jī)(以 TIM 為例)
鑒于以上的分析基显,只需要將 Info.plist 中的對應(yīng)版本號提高,并且加入新的啟動圖即可實現(xiàn)三方應(yīng)用適配新機(jī)善炫。
1. 砸殼
由于所有在 App Store 上的應(yīng)用均被蘋果加殼撩幽,并且我們將來需要修改 Info.plist 文件,所以必須要重簽名箩艺。因此我們所修改的 TIM 需要砸殼處理窜醉。
砸殼方式有很多種,此文不闡述艺谆。
參考:Clutch榨惰、frida-ios-dump 等等。
2. 修改 Info.plist 中的版本號
由于 Info.plist 中版本號有點復(fù)雜擂涛,有部分為內(nèi)部版本號(需要查詢)读串,所以我將已經(jīng)適配好 Xs Max 的應(yīng)用拿來做參考聊记,進(jìn)行數(shù)據(jù)覆蓋。
此處使用的是 WeChat 7.0.0 的 Info.plist 作為參考依據(jù)恢暖。將上述提到的 7 個字段的值排监,從微信的 Info.plist 文件里提取出來,覆蓋到 TIM 的 Info.plist 文件中杰捂。分別是:DTSDKName
舆床、DTXcode
、DTSDKBuild
嫁佳、BuildMachineOSBuild
挨队、DTPlatformVersion
、DTXcodeBuild
蒿往、DTPlatformBuild
盛垦。
除版本號外,Info.plist 也包含支持設(shè)備的硬件號瓤漏,字段為
UISupportedDevices
通過助手類軟件可以讀出 Xs Max 設(shè)備號為
iPhone11,6
腾夯,將其加入到UISupportedDevices
字段中,防止安裝時報錯(不支持此設(shè)備)
3. 添加啟動圖
經(jīng)過與微信的比較蔬充,發(fā)現(xiàn) Xs Max 和 Xr 的啟動圖分別是:
名稱 | 大小 | 機(jī)型 |
---|---|---|
LaunchImage-1200-Portrait-1792h@2x.png | 828 x 1792 | Xr |
LaunchImage-1200-Portrait-2688h@3x.png | 1242 x 2688 | Xs Max |
終于祭出了自己的美工技術(shù)了蝶俱。提取 X 的啟動圖,修改為上述兩個文件饥漫,并添加到包中榨呆。
打開 Info.plist 文件,比較后在 UILaunchImages
的數(shù)組里添加兩項啟動圖配置:
<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. 重簽名
重簽名的方式有很多種积蜻,此文不再詳述。
至于重簽名后無法正常推送皿哨,不在此文的探討范圍浅侨,我就不詳細(xì)說明了纽谒。
對比圖
左側(cè)為適配前证膨,右側(cè)為適配后
適配后的 UI 變得非常的細(xì)膩。舒服~~
總結(jié)
Xcode 在打包的時候講打包環(huán)境信息(各種版本號)保存到 Info.plist 中鼓黔。iOS 運(yùn)行應(yīng)用時央勒,通過 Info.plist 中的版本號進(jìn)行比較,來判斷開發(fā)者是否適配了新設(shè)備澳化,從而判斷是否需要兼容模式運(yùn)行此應(yīng)用崔步。