iOS11、iPhone X、Xcode9 適配指南

来源:http://www.cnblogs.com/allencelee/archive/2017/11/13/7826430.html
-Advertisement-
Play Games

更新iOS11後,發現有些地方需要做適配,整理後按照優先順序分為以下三類: 1.單純升級iOS11後造成的變化; 2.Xcode9 打包後造成的變化; 3.iPhoneX的適配 一、單純升級iOS11後造成的變化 1. 升級後,發現某個擁有tableView的界面錯亂,組間距和contentInset ...


更新iOS11後,發現有些地方需要做適配,整理後按照優先順序分為以下三類:

1.單純升級iOS11後造成的變化;

2.Xcode9 打包後造成的變化;

3.iPhoneX的適配

一、單純升級iOS11後造成的變化

1. 升級後,發現某個擁有tableView的界面錯亂,組間距和contentInset錯亂,因為iOS11中 UIViewController 的 automaticallyAdjustsScrollViewInsets 屬性被廢棄了,因此當tableView超出安全區域時,系統自動會調整SafeAreaInsets值,進而影響adjustedContentInset值

 1 // 有些界面以下使用代理方法來設置,發現並沒有生效
 2 - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section;
 3 - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section;
 4 // 這樣的原理是因為之前只是實現了高度的代理方法,卻沒有實現View的代理方法,iOS10及以前這麼寫是沒問題的,iOS11開啟了行高估算機制引起的bug,因此有以下幾種解決方法:
 5 // 解決方法一:添加實現View的代理方法,只有實現下麵兩個方法,方法 (CGFloat)tableView: heightForFooterInSection: 才會生效
 6 - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
 7     return nil;
 8 }
 9 - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
10     return nil;
11 }
12 // 解決方法二:直接使用tableView屬性進行設置,修複該UI錯亂
13 self.tableView.sectionHeaderHeight = 0;
14 self.tableView.sectionFooterHeight = 5;
15 [_optionTableView setContentInset:UIEdgeInsetsMake(-35, 0, 0, 0)];
16 // 解決方法三:添加以下代碼關閉估算行高
17 self.tableView.estimatedRowHeight = 0;
18 self.tableView.estimatedSectionHeaderHeight = 0;
19 self.tableView.estimatedSectionFooterHeight = 0;

2. 如果使用了Masonry 進行佈局,就要適配safeArea

if ([UIDevice currentDevice].systemVersion.floatValue >= 11.0) {
    make.edges.equalTo(self.view.safeAreaInsets);
} else {
    make.edges.equalTo(self.view);
}

3. 對於IM的發送原圖功能,iOS11啟動全新的HEIC 格式的圖片,iPhone7以上設備+iOS11排出的live照片是".heic"格式圖片,同一張live格式的圖片,iOS10發送就沒問題(轉成了jpg),iOS11就不行

微信的處理方式是一比一轉化成 jpg 格式

QQ和釘釘的處理方式是直接壓縮,即使是原圖也壓縮為非原圖

最終採取的是微信的方案,使用以下代碼轉成jpg格式

// 0.83能保證壓縮前後圖片大小是一致的
// 造成不一致的原因是圖片的bitmap一個是8位的,一個是16位的
imageData = UIImageJPEGRepresentation([UIImage imageWithData:imageData], 0.83);

二、使用Xcode9 編譯後發現的問題

1. 發現“fastSocket”第三方報錯,具體原因是缺少C99的頭文件,引入“#include”即可

2. 導航欄的新特性

原生的搜索欄樣式發生改變

1501122-116e05d789ade206.png

右邊為iOS11樣式,搜索區域高度變大,字體變大

查看 API 後發現,iOS11後將 searchController 賦值給了 NavigationItem,通過屬性 hidesSearchBarWhenScrolling 可以控制搜索欄是否在滑動的時候進行隱藏和顯示

// A view controller that will be shown inside of a navigation controller can assign a UISearchController to this property to display the search controller’s search bar in its containing navigation controller’s navigation bar.
@property (nonatomic, retain, nullable) UISearchController *searchController API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos);
// If this property is true (the default), the searchController’s search bar will hide as the user scrolls in the top view controller’s scroll view. If false, the search bar will remain visible and pinned underneath the navigation bar.
@property (nonatomic) BOOL hidesSearchBarWhenScrolling API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos);

另外,UINavigationBar 新增屬性 BOOL值 prefersLargeTitles 來實現下麵的效果,並可以通過 largeTitleTextAttributes 來設置大標題的文本樣式。設置大標題之後,導航欄的高度就會由之前的64pt變成 96pt,如果項目中有直接寫死的高度或者隱藏導航欄之類的操作,就需要適配一下

有個界面使用到了導航欄按鈕相關的frame,也發生了UI錯亂,查看UI層級關係後發現,iOS11以前是直接把按鈕加到了UINavigationBar上面,而iOS11則是先將按鈕加到了_UITAMICAdaptorView,再加到_UIButtonBarStackView、_UINavigationBarContentView,接著才是UINavigationBar。因此如果需要獲取導航欄按鈕 frame 或者 superView,這裡需要專門做下適配

1501122-5638fb3c96f7f7c7.png

iOS10及以下版本導航欄按鈕層級關係圖

1501122-8dec0fb8db68700a.png

iOS11導航欄按鈕層級關係圖

三、iPhone X的適配

下載完Xcode9之後,第一件事自然是在 iPhone X(模擬器)上過把癮,然後編譯後就發現報錯了

由於iPhone X的狀態欄是和其他版本手機差異比較大的,因此api 變化也比較大

先後做了以下適配

適配點一:項目中使用狀態欄中圖標判斷當前網路的具體狀態

1501122-2092f05dbbf74b59.png

出錯代碼

1 列印的 Log 報出以下錯誤: Trapped uncaught exception 'NSUnknownKeyException', reason: '[ valueForUndefinedKey:]: this class is not key value coding-compliant for the key foregroundView.'

1501122-2e84d95ab983febb.png

iPhone X

11.png

其他手機

使用 runtime 列印其所有屬性,發現以下差異

// 測試代碼
#import NSMutableString *resultStr = [NSMutableString string];
//獲取指定類的Ivar列表及Ivar個數
unsigned int count = 0;
Ivar *member = class_copyIvarList([[application valueForKeyPath:@"_statusBar"] class], &count);
     
for(int i = 0; i < count; i++){
    Ivar var = member[i];
    //獲取Ivar的名稱
    const char *memberAddress = ivar_getName(var);
    //獲取Ivar的類型
    const char *memberType = ivar_getTypeEncoding(var);
    NSString *str = [NSString stringWithFormat:@"key = %s             type = %s \n",memberAddress,memberType];
      [resultStr appendString:str];
}
NSLog(@"%@", resultStr);

// 其他版本的手機
key = _inProcessProvider            type = @""
key = _showsForeground              type = B
key = _backgroundView               type = @"UIStatusBarBackgroundView"
key = _doubleHeightLabel            type = @"UILabel"
key = _doubleHeightLabelContainer   type = @"UIView"
key = _currentDoubleHeightText      type = @"NSString"
key = _currentRawData               type = {超長。。}
key = _interruptedAnimationCompositeViews  type = @"NSMutableArray"
key = _newStyleBackgroundView       type = @"UIStatusBarBackgroundView"
key = _newStyleForegroundView       type = @"UIStatusBarForegroundView"
key = _slidingStatusBar             type = @"UIStatusBar"
key = _styleAttributes              type = @"UIStatusBarStyleAttributes"
key = _waitingOnCallbackAfterChangingStyleOverridesLocally  type = B
key = _suppressGlow                 type = B
key = _translucentBackgroundAlpha   type = d
key = _showOnlyCenterItems          type = B
key = _foregroundViewShouldIgnoreStatusBarDataDuringAnimation  type = B
key = _tintColor                    type = @"UIColor"
key = _lastUsedBackgroundColor      type = @"UIColor"
key = _nextTintTransition           type = @"UIStatusBarStyleAnimationParameters"
key = _overrideHeight               type = @"NSNumber"
key = _disableRasterizationReasons  type = @"NSMutableSet"
key = _timeHidden                   type = B
key = _statusBarWindow              type = @"UIStatusBarWindow"
// iPhone X
key = _statusBar ; type = @"_UIStatusBar"
// 因此可見iPhone X的狀態欄是多嵌套了一層,多取一次即可,最終適配代碼為:
NSArray *children;
// 不能用 [[self deviceVersion] isEqualToString:@"iPhone X"] 來判斷,因為iPhone X 的模擬器不會返回 iPhone X
    if ([[application valueForKeyPath:@"_statusBar"] isKindOfClass:NSClassFromString(@"UIStatusBar_Modern")]) {
        children = [[[[application valueForKeyPath:@"_statusBar"] valueForKeyPath:@"_statusBar"] valueForKeyPath:@"foregroundView"] subviews];
    } else {
        children = [[[application valueForKeyPath:@"_statusBar"] valueForKeyPath:@"foregroundView"] subviews];
    }

警告以上處理,代碼看起來是不報錯了,然而!!具體看了下代碼發現並不生效!因為從iPhone X取出來之後只有view層級的信息,因此採用以下方法確定2G/3G/4G,從API上目測是有效的

NSArray *typeStrings2G = @[CTRadioAccessTechnologyEdge,
                               CTRadioAccessTechnologyGPRS,
                               CTRadioAccessTechnologyCDMA1x];
     
    NSArray *typeStrings3G = @[CTRadioAccessTechnologyHSDPA,
                               CTRadioAccessTechnologyWCDMA,
                               CTRadioAccessTechnologyHSUPA,
                               CTRadioAccessTechnologyCDMAEVDORev0,
                               CTRadioAccessTechnologyCDMAEVDORevA,
                               CTRadioAccessTechnologyCDMAEVDORevB,
                               CTRadioAccessTechnologyeHRPD];
     
    NSArray *typeStrings4G = @[CTRadioAccessTechnologyLTE];
    // 該 API 在 iOS7 以上系統才有效
    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
        CTTelephonyNetworkInfo *teleInfo= [[CTTelephonyNetworkInfo alloc] init];
        NSString *accessString = teleInfo.currentRadioAccessTechnology;
        if ([typeStrings4G containsObject:accessString]) {
            NSLog(@"4G網路");
        } else if ([typeStrings3G containsObject:accessString]) {
            NSLog(@"3G網路");
        } else if ([typeStrings2G containsObject:accessString]) {
            NSLog(@"2G網路");
        } else {
            NSLog(@"未知網路");
        }
    } else {
        NSLog(@"未知網路");
    }

適配點二:解決這個問題後項目跑起來發現,整個app界面上下各空出大概40pt的高度

22.png

經常從 Github 上下載項目把玩的老司機們都知道,有些老項目在模擬器上跑起來之後也會只有 iPhone 4(320480)的佈局空間,造成這個的原因是啟動圖使用 Launch Images Source 設置的時候沒有勾選並設置對應的圖片(11252436),解決方法如下

12.png

但是即使按照上面的操作進行之後,會發現底部 UITabBar 依舊是高出一些高度,查看層級關係後發現,同樣是由於安全區的原因,UITabBar 高度由49pt變成了83pt,因此這裡也要對iPhone X 及其模擬器進行適配

適配點三:iPhone X 只有 faceID,沒有touchID,如果in的應用有使用到 touchID 解鎖的地方,這裡要根據機型進行相應的適配

適配點四:之前有偷懶的直接使用20替代狀態欄高度,這些坑都要通過重新獲取狀態欄高度

CGRectGetHeight([UIApplication sharedApplication].statusBarFrame)

適配點五:然而iPhone X更大的坑是屏幕的適配

首先看下屏幕尺寸

123.png

這張圖反映出不少信息:

iPhone X的寬度雖然和7是一樣的,但是高度多出145pt

使用三倍圖是重點,而且一般認為肉眼所能所能識別的最高的屏幕密度是300ppi,iPhone X已達到458ppi(查證發現三星galaxy系列的屏幕密度是522ppi)

在設計方面,蘋果官方文檔human-interface-guidelines有明確要求,下麵結合圖例進行說明:

111.png

展示出來的設計佈局要求填滿整個屏幕

222.png

填滿的同時要註意控制項不要被大圓角和感測器部分所遮擋

33.png

安全區域以外的部分不允許有任何與用戶交互的控制項

上面這張圖內含信息略多

頭部導航欄不予許進行用戶交互的,意味著下麵這兩種情況 Apple 官方是不允許的

底部虛擬區是替代了傳統home鍵,高度為34pt,通過上滑可呼起多任務管理,考慮到手勢衝突,這部分也是不允許有任何可交互的控制項,但是設計的背景圖要覆蓋到非安全區域

狀態欄在非安全區域,文檔中也提到,除非可以通過隱藏狀態欄給用戶帶來額外的價值,否則最好把狀態欄還給用戶

不要讓 界面中的元素 干擾底部的主屏幕指示器

重覆使用現有圖片時,註意長寬比差異。iPhone X 與常規 iPhone 的屏幕長寬比不同,因此,全屏的 4.7 寸屏圖像在 iPhone X 上會出現裁切或適配寬度顯示。所以,這部分的視圖需要根據設備做出適配。

334.png

註意類似占點陣圖的適配

適配點六:橫屏適配

關於 safe area,使用 safeAreaLayoutGuide 和 safeAreaInset就能解決大部分問題,但是橫屏下還可能會產生一些問題,需要額外適配

121.png

產生這個原因代碼是:[headerView.contentView setBackgroundColor:[UIColor headerFooterColor]],這個寫法看起來沒錯,但是只有在 iPhone X上有問題

212.png

通過設置該屬性,內容視圖嵌入到了safe area中,但是contentView沒有

解決方法:設置backgroundView顏色 [headerView.backgroundView setBackgroundColor:[UIColor headerFooterColor]]

適配點七:設備信息

if ([deviceString isEqualToString:@"iPhone10,1"])   return @"國行(A1863)、日行(A1906)iPhone 8";
if ([deviceString isEqualToString:@"iPhone10,4"])   return @"美版(Global/A1905)iPhone 8";
if ([deviceString isEqualToString:@"iPhone10,2"])   return @"國行(A1864)、日行(A1898)iPhone 8 Plus";
if ([deviceString isEqualToString:@"iPhone10,5"])   return @"美版(Global/A1897)iPhone 8 Plus";
if ([deviceString isEqualToString:@"iPhone10,3"])   return @"國行(A1865)、日行(A1902)iPhone X";
if ([deviceString isEqualToString:@"iPhone10,6"])   return @"美版(Global/A1901)iPhone X";

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Web開發如今是如日中天,熱的發燙。那我們應該怎麼學習呢?這不光是初學者,很多學了幾年的人也會有些迷茫或者彷徨,大家也都知道不斷學習是不可避免的,不學習肯定要掉隊;那怎麼學效率更高,那些是坑,那些是路,每個人可能都有自己的簡介。作為一個程式猿,從兩個方面說說我的想法: 學習方面 1.基礎部分,主要就 ...
  • 本人整理的筆記,來自有道雲筆記: http://note.youdao.com/noteshare?id=12186e338fe5b73cba3cc5876ff167e8&sub=9049D3EE84474F4592905E5B91B13118 ...
  • 對於移動端這塊,筆者之前一直都是進行iOS開發的,也從來沒用過Java。但是因為進入了Google Android全國大學生移動互聯網創新挑戰賽(進入官網)的總決賽(筆者“西部電腦教育提升計劃”的項目被直接推薦進入決賽),這個比賽要求一定要提交apk程式,所以我不得不趕緊學習一下Android開發 ...
  • 筆者今年做了一個和人臉有關的android產品,主要是獲取攝像頭返回的預覽數據流,判斷該數據流是否包含了人臉,有人臉時顯示攝像頭預覽框,無人臉時攝像頭預覽框隱藏,看上去這個功能並不複雜,其實在開發過程中,遇到的問題也不多,全部都處理了,在正式推出前,這個產品在公司內部也測試了幾個月,也沒發現bug, ...
  • 調用系統圖庫: Intent intent = new Intent(Intent.ACTION_PICK,MediaStore.Images.Media.EXTERNAL_CONTENT_URI); startActivityForResult(intent, REQUEST_CODE_IMAGE... ...
  • Hello Android 代碼 備註 //創建對象 //設置對話框標題 //設置圖標 //設置內容 //設置點擊返回鍵對話框不消失 //顯示對話框 控制項id.setOnClickListener{ ​ ///事件內容 } 對話框對象AlertDialog 對話框引用android.support. ...
  • Ps:7-10月 完成公司兩個app項目上架。漏掉的總結 開始慢慢補上。 一、寫一個Activity的管理類 1、單例模式,以棧(先進後出)的形式存儲Activity對象 2、給AppManager管理類添加幾個常用方法。 (1)、添加Activity對象 (2)、結束當前Activity對象,即棧 ...
  • 轉載請標明出處:http://blog.csdn.net/zhaoyanjun6/article/details/78113868 本文出自 "【趙彥軍的博客】" 一:概述 如果不瞭解插件開發基礎的同學可以先看, "Android Studio 插件開發詳解一:入門練手" "Android Stud ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...