適配iOS11&iPhoneX

1. 升級(jí)后勾拉,發(fā)現(xiàn)某個(gè)擁有tableView的界面錯(cuò)亂,組間距和contentInset錯(cuò)亂盗温,因?yàn)閕OS11中 UIViewController 的 automaticallyAdjustsScrollViewInsets 屬性被廢棄了藕赞,因此當(dāng)tableView超出安全區(qū)域時(shí),系統(tǒng)自動(dòng)會(huì)調(diào)整SafeAreaInsets值卖局,進(jìn)而影響adjustedContentInset值
// 有些界面以下使用代理方法來(lái)設(shè)置找默,發(fā)現(xiàn)并沒(méi)有生效
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section;
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section;
// 這樣的原理是因?yàn)橹爸皇菍?shí)現(xiàn)了高度的代理方法,卻沒(méi)有實(shí)現(xiàn)View的代理方法吼驶,iOS10及以前這么寫是沒(méi)問(wèn)題的惩激,iOS11開啟了行高估算機(jī)制引起的bug,因此有以下幾種解決方法:
// 解決方法一:添加實(shí)現(xiàn)View的代理方法蟹演,只有實(shí)現(xiàn)下面兩個(gè)方法风钻,方法 (CGFloat)tableView: heightForFooterInSection: 才會(huì)生效
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
    return nil;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    return nil;
}
// 解決方法二:直接使用tableView屬性進(jìn)行設(shè)置,修復(fù)該UI錯(cuò)亂
self.tableView.sectionHeaderHeight = 0;
self.tableView.sectionFooterHeight = 5;
[_optionTableView setContentInset:UIEdgeInsetsMake(-35, 0, 0, 0)];
// 解決方法三:添加以下代碼關(guān)閉估算行高
self.tableView.estimatedRowHeight = 0;
self.tableView.estimatedSectionHeaderHeight = 0;
self.tableView.estimatedSectionFooterHeight = 0;
2.如果使用了Masonry 進(jìn)行布局,就要適配safeArea
if ([UIDevice currentDevice].systemVersion.floatValue >= 11.0) {
    make.edges.equalTo(self.view.safeAreaInsets);
} else {
    make.edges.equalTo(self.view);
}
3.對(duì)于IM的發(fā)送原圖功能酒请,iOS11啟動(dòng)全新的HEIC 格式的圖片骡技,iPhone7以上設(shè)備+iOS11排出的live照片是".heic"格式圖片,同一張live格式的圖片,iOS10發(fā)送就沒(méi)問(wèn)題(轉(zhuǎn)成了jpg)布朦,iOS11就不行
微信的處理方式是一比一轉(zhuǎn)化成 jpg 格式
QQ和釘釘?shù)奶幚矸绞绞侵苯訅嚎s囤萤,即使是原圖也壓縮為非原圖
最終采取的是微信的方案,使用以下代碼轉(zhuǎn)成jpg格式
// 0.83能保證壓縮前后圖片大小是一致的
// 造成不一致的原因是圖片的bitmap一個(gè)是8位的是趴,一個(gè)是16位的
imageData = UIImageJPEGRepresentation([UIImage imageWithData:imageData], 0.83);
4.導(dǎo)航欄的新特性

原生的搜索欄樣式發(fā)生改變

導(dǎo)航欄

查看 API 后發(fā)現(xiàn)涛舍,iOS11后將 searchController 賦值給了 NavigationItem,通過(guò)屬性 hidesSearchBarWhenScrolling 可以控制搜索欄是否在滑動(dòng)的時(shí)候進(jìn)行隱藏和顯示

// 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 來(lái)實(shí)現(xiàn)下面的效果富雅,并可以通過(guò) largeTitleTextAttributes 來(lái)設(shè)置大標(biāo)題的文本樣式。設(shè)置大標(biāo)題之后肛搬,導(dǎo)航欄的高度就會(huì)由之前的64pt變成 96pt没佑,如果項(xiàng)目中有直接寫死的高度或者隱藏導(dǎo)航欄之類的操作,就需要適配一下

有個(gè)界面使用到了導(dǎo)航欄按鈕相關(guān)的frame温赔,也發(fā)生了UI錯(cuò)亂蛤奢,查看UI層級(jí)關(guān)系后發(fā)現(xiàn),iOS11以前是直接把按鈕加到了UINavigationBar上面陶贼,而iOS11則是先將按鈕加到了_UITAMICAdaptorView远剩,再加到_UIButtonBarStackView、_UINavigationBarContentView骇窍,接著才是UINavigationBar。因此如果需要獲取導(dǎo)航欄按鈕 frame 或者 superView锥余,這里需要專門做下適配

導(dǎo)航欄的層級(jí)關(guān)系

5.iPhone X的適配

下載完Xcode9之后腹纳,第一件事自然是在 iPhone X(模擬器)上過(guò)把癮,然后編譯后就發(fā)現(xiàn)報(bào)錯(cuò)了

由于iPhone X的狀態(tài)欄是和其他版本手機(jī)差異比較大的驱犹,因此api 變化也比較大

先后做了以下適配

適配點(diǎn)一:項(xiàng)目中使用狀態(tài)欄中圖標(biāo)判斷當(dāng)前網(wǎng)絡(luò)的具體狀態(tài)

出錯(cuò)代碼
打印的 Log 報(bào)出以下錯(cuò)誤: Trapped uncaught exception 'NSUnknownKeyException', reason: '[ valueForUndefinedKey:]: this class is not key value coding-compliant for the key foregroundView.'

使用 runtime 打印其所有屬性嘲恍,發(fā)現(xiàn)以下差異

// 測(cè)試代碼
#import NSMutableString *resultStr = [NSMutableString string];
//獲取指定類的Ivar列表及Ivar個(gè)數(shù)
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);
// 其他版本的手機(jī)
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 = {超長(zhǎng)。雄驹。}
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"
// 因此可見(jiàn)iPhone X的狀態(tài)欄是多嵌套了一層佃牛,多取一次即可,最終適配代碼為:
NSArray *children;
// 不能用 [[self deviceVersion] isEqualToString:@"iPhone X"] 來(lái)判斷医舆,因?yàn)閕Phone X 的模擬器不會(huì)返回 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];
    }

警告以上處理俘侠,代碼看起來(lái)是不報(bào)錯(cuò)了,然而J呓爷速!具體看了下代碼發(fā)現(xiàn)并不生效!因?yàn)閺膇Phone X取出來(lái)之后只有view層級(jí)的信息霞怀,因此采用以下方法確定2G/3G/4G惫东,從API上目測(cè)是有效的

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

適配點(diǎn)二:解決這個(gè)問(wèn)題后項(xiàng)目跑起來(lái)發(fā)現(xiàn),整個(gè)app界面上下各空出大概40pt的高度


經(jīng)常從 Github 上下載項(xiàng)目把玩的老司機(jī)們都知道,有些老項(xiàng)目在模擬器上跑起來(lái)之后也會(huì)只有 iPhone 4(320480)的布局空間廉沮,造成這個(gè)的原因是啟動(dòng)圖使用 Launch Images Source 設(shè)置的時(shí)候沒(méi)有勾選并設(shè)置對(duì)應(yīng)的圖片(11252436)颓遏,解決方法如下


但是即使按照上面的操作進(jìn)行之后,會(huì)發(fā)現(xiàn)底部 UITabBar 依舊是高出一些高度滞时,查看層級(jí)關(guān)系后發(fā)現(xiàn)叁幢,同樣是由于安全區(qū)的原因,UITabBar 高度由49pt變成了83pt漂洋,因此這里也要對(duì)iPhone X 及其模擬器進(jìn)行適配

適配點(diǎn)三:iPhone X 只有 faceID遥皂,沒(méi)有touchID,如果in的應(yīng)用有使用到 touchID 解鎖的地方刽漂,這里要根據(jù)機(jī)型進(jìn)行相應(yīng)的適配

適配點(diǎn)四:之前有偷懶的直接使用20替代狀態(tài)欄高度演训,這些坑都要通過(guò)重新獲取狀態(tài)欄高度

CGRectGetHeight([UIApplication sharedApplication].statusBarFrame)

適配點(diǎn)五:然而iPhone X更大的坑是屏幕的適配
首先看下屏幕尺寸


這張圖反映出不少信息:
iPhone X的寬度雖然和7是一樣的,但是高度多出145pt
使用三倍圖是重點(diǎn)贝咙,而且一般認(rèn)為肉眼所能所能識(shí)別的最高的屏幕密度是300ppi样悟,iPhone X已達(dá)到458ppi(查證發(fā)現(xiàn)三星galaxy系列的屏幕密度是522ppi)
在設(shè)計(jì)方面,蘋果官方文檔human-interface-guidelines有明確要求庭猩,下面結(jié)合圖例進(jìn)行說(shuō)明:

展示出來(lái)的設(shè)計(jì)布局要求填滿整個(gè)屏幕
填滿的同時(shí)要注意控件不要被大圓角和傳感器部分所遮擋
安全區(qū)域以外的部分不允許有任何與用戶交互的控件

上面這張圖內(nèi)含信息略多
頭部導(dǎo)航欄不予許進(jìn)行用戶交互的窟她,意味著下面這兩種情況 Apple 官方是不允許的
底部虛擬區(qū)是替代了傳統(tǒng)home鍵,高度為34pt蔼水,通過(guò)上滑可呼起多任務(wù)管理震糖,考慮到手勢(shì)沖突,這部分也是不允許有任何可交互的控件趴腋,但是設(shè)計(jì)的背景圖要覆蓋到非安全區(qū)域
狀態(tài)欄在非安全區(qū)域吊说,文檔中也提到,除非可以通過(guò)隱藏狀態(tài)欄給用戶帶來(lái)額外的價(jià)值优炬,否則最好把狀態(tài)欄還給用戶
不要讓 界面中的元素 干擾底部的主屏幕指示器
重復(fù)使用現(xiàn)有圖片時(shí)颁井,注意長(zhǎng)寬比差異。iPhone X 與常規(guī) iPhone 的屏幕長(zhǎng)寬比不同蠢护,因此雅宾,全屏的 4.7 寸屏圖像在 iPhone X 上會(huì)出現(xiàn)裁切或適配寬度顯示。所以葵硕,這部分的視圖需要根據(jù)設(shè)備做出適配眉抬。


注意類似占位圖的適配

適配點(diǎn)六:橫屏適配

關(guān)于 safe area,使用 safeAreaLayoutGuide 和 safeAreaInset就能解決大部分問(wèn)題懈凹,但是橫屏下還可能會(huì)產(chǎn)生一些問(wèn)題吐辙,需要額外適配



通過(guò)設(shè)置該屬性,內(nèi)容視圖嵌入到了safe area中蘸劈,但是contentView沒(méi)有
解決方法:設(shè)置backgroundView顏色 [headerView.backgroundView setBackgroundColor:[UIColor headerFooterColor]]
適配點(diǎn)七:設(shè)備信息

if ([deviceString isEqualToString:@"iPhone10,1"])   return @"國(guó)行(A1863)昏苏、日行(A1906)iPhone 8";
if ([deviceString isEqualToString:@"iPhone10,4"])   return @"美版(Global/A1905)iPhone 8";
if ([deviceString isEqualToString:@"iPhone10,2"])   return @"國(guó)行(A1864)、日行(A1898)iPhone 8 Plus";
if ([deviceString isEqualToString:@"iPhone10,5"])   return @"美版(Global/A1897)iPhone 8 Plus";
if ([deviceString isEqualToString:@"iPhone10,3"])   return @"國(guó)行(A1865)、日行(A1902)iPhone X";
if ([deviceString isEqualToString:@"iPhone10,6"])   return @"美版(Global/A1901)iPhone X";
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末贤惯,一起剝皮案震驚了整個(gè)濱河市洼专,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌孵构,老刑警劉巖屁商,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異颈墅,居然都是意外死亡蜡镶,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門恤筛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)官还,“玉大人,你說(shuō)我怎么就攤上這事毒坛⊥祝” “怎么了?”我有些...
    開封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵煎殷,是天一觀的道長(zhǎng)屯伞。 經(jīng)常有香客問(wèn)我,道長(zhǎng)豪直,這世上最難降的妖魔是什么劣摇? 我笑而不...
    開封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮弓乙,結(jié)果婚禮上末融,老公的妹妹穿的比我還像新娘。我一直安慰自己唆貌,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開白布垢乙。 她就那樣靜靜地躺著锨咙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪追逮。 梳的紋絲不亂的頭發(fā)上酪刀,一...
    開封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音钮孵,去河邊找鬼骂倘。 笑死,一個(gè)胖子當(dāng)著我的面吹牛巴席,可吹牛的內(nèi)容都是我干的历涝。 我是一名探鬼主播,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼荧库!你這毒婦竟也來(lái)了堰塌?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤分衫,失蹤者是張志新(化名)和其女友劉穎场刑,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蚪战,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡牵现,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了邀桑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瞎疼。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖概漱,靈堂內(nèi)的尸體忽然破棺而出丑慎,到底是詐尸還是另有隱情,我是刑警寧澤瓤摧,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布竿裂,位于F島的核電站,受9級(jí)特大地震影響照弥,放射性物質(zhì)發(fā)生泄漏腻异。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一这揣、第九天 我趴在偏房一處隱蔽的房頂上張望悔常。 院中可真熱鬧,春花似錦给赞、人聲如沸机打。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)残邀。三九已至,卻和暖如春柑蛇,著一層夾襖步出監(jiān)牢的瞬間芥挣,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工耻台, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留空免,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓盆耽,卻偏偏與公主長(zhǎng)得像蹋砚,于是被迫代替她去往敵國(guó)和親扼菠。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 作者:張?jiān)掳腈溄樱篽ttp://www.reibang.com/p/c355cc4b12c2來(lái)源:簡(jiǎn)書著作權(quán)歸作者...
    KingLionsFrank閱讀 935評(píng)論 0 0
  • 博客傳送門前陣子項(xiàng)目開發(fā)忙成狗都弹,就一直沒(méi)做iOS11的適配娇豫,直到XcodeGM版發(fā)布后主卫,我胸有成竹的在iPhone...
    張?jiān)掳?/span>閱讀 30,059評(píng)論 55 239
  • 導(dǎo)航欄 導(dǎo)航欄高度的變化 iOS11之前導(dǎo)航欄默認(rèn)高度為64pt(這里高度指statusBar + Navigat...
    西門淋雨閱讀 737評(píng)論 0 0
  • 導(dǎo)航欄導(dǎo)航欄高度的變化iOS11之前導(dǎo)航欄默認(rèn)高度為64pt(這里高度指statusBar + Navigatio...
    貝勒老爺閱讀 655評(píng)論 0 5
  • 我有時(shí)一直看到網(wǎng)絡(luò)上辉浦,有人為了一件事對(duì)噴開罵,我有時(shí)看看這些人的言論赖阻,真是覺(jué)得非常好笑框杜,網(wǎng)絡(luò)上我看不見(jiàn)你浦楣,你看不見(jiàn)...
    餓素5zp閱讀 163評(píng)論 0 0