iOS 11 適配集錦

iOS 11適配源碼 Demo地址

安全區(qū)域的適配

用Xcode 9 創(chuàng)建storyboard或者xib時(shí),最低版本支持iOS 8時(shí)會(huì)報(bào): Safe Area Layout Guide before iOS 9.0 如圖:

原因:在iOS7中引入的Top Layout Guide和Bottom Layout Guide,這些布局指南在iOS 11中被棄用,取而代之的是Safe Area Layout Guide.

當(dāng)一個(gè)Viewcontroller 被嵌入到UINavigationcontroller 、Tab bar 或者ToolBar 中時(shí), 我們可以使用 Top Layout GuideBottom Layout Guide 讓 view根 據(jù)上下錨點(diǎn)自適應(yīng)內(nèi)容。如圖:

在iOS 11中取而代之的是Safe Area Layout Guide,在iOS11中蘋果用單獨(dú)的Safe Area屬性代替了上面的屬性.安全區(qū)域限制于頂部和底部的錨點(diǎn)。如圖:

解決辦法:適配最低支持版本iOS 8,將圖中的 Use Safe Area Layout Guide 取消即可

可以通過一個(gè)新的屬性:addtionalSafeAreaInsets來改變safeAreaInsets的值,當(dāng)你的viewController改變了它的safeAreaInsets值時(shí),有兩種方式獲取到回調(diào):

UIView.safeAreaInsetsDidChange()
UIViewController.viewSafeAreaInsetsDidChange()

如果你的APP中是自定義的Navigationbar,隱藏掉系統(tǒng)的Navigationbar,并且tableView的frame為(0, 0, kSCREEN_WIDTH, kSCREEN_HEIGHT)開始,那么系統(tǒng)會(huì)自動(dòng)調(diào)整SafeAreaInsets值為(20,0,0,0),如果使用了系統(tǒng)的navigationbar,那么SafeAreaInsets值為(64,0,0,0),如果也使用了系統(tǒng)的tabbar,那么SafeAreaInsets值為(64,0,49,0)

UIScrollView、UITableView、UICollectionView適配

UITableView

Tableview莫名奇妙的偏移20pt或者64pt, 原因是iOS11棄用了automaticallyAdjustsScrollViewInsets屬性,取而代之的是UIScrollView新增了contentInsetAdjustmentBehavior屬性,這一切的罪魁禍?zhǔn)锥际切乱氲膕afeArea

適配:

// 定義宏
#define  adjustsScrollViewInsets(scrollView)\
do {\
_Pragma("clang diagnostic push")\
_Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"")\
if ([scrollView respondsToSelector:NSSelectorFromString(@"setContentInsetAdjustmentBehavior:")]) {\
    NSMethodSignature *signature = [UIScrollView instanceMethodSignatureForSelector:@selector(setContentInsetAdjustmentBehavior:)];\
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];\
    NSInteger argument = 2;\
    invocation.target = scrollView;\
    invocation.selector = @selector(setContentInsetAdjustmentBehavior:);\
    [invocation setArgument:&argument atIndex:2];\
    [invocation retainArguments];\
    [invocation invoke];\
}\
_Pragma("clang diagnostic pop")\
} while (0)

如果你使用了Masonry,那么你需要適配safeArea

if (@available(iOS 11.0, *)) {
    make.edges.equalTo()(self.view.safeAreaInsets)
} else {
    make.edges.equalTo()(self.view)
}
  • 首先結(jié)構(gòu)發(fā)生了變化:對(duì)比

適配:設(shè)置TableView的高度為全屏,會(huì)自動(dòng)適配。

  • 有點(diǎn)Eclipse的味道了,遵守代理后,點(diǎn)擊fix會(huì)自動(dòng)填充完所需方法

自動(dòng)實(shí)現(xiàn)所需方法效果(貌似實(shí)現(xiàn)了很多不常用的):

  • 代理方法的優(yōu)化:

iOS 11之前不設(shè)置sectionHeaderView或者sectionFooterView會(huì)走設(shè)置高度的方法。
iOS 11 如果不實(shí)現(xiàn)下面這兩個(gè)方法,不會(huì)走設(shè)置高度的方法,即:設(shè)置高度失效。

當(dāng)TableView時(shí)分組樣式Grouped,設(shè)置高度為不等0的很小的數(shù)也會(huì)失效。所以必須實(shí)現(xiàn)下面兩個(gè)方法和并設(shè)置高度。

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section;

- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section

  • iOS 11默認(rèn)開啟了Self-Sizing(在WWDC 2017 session204 Updating Your App for iOS 11 中有介紹),也是造成上面代理方法優(yōu)化的問題的根本原因,同時(shí)在獲取TableView的ContentSize也不再準(zhǔn)確的大小。均是UITableViewAutomaticDimension 預(yù)估高度造成。

解決辦法:

estimatedRowHeight、estimatedSectionHeaderHeight、estimatedSectionFooterHeight均設(shè)置為0,即將默認(rèn)開啟關(guān)閉。

UIScrollView

  • scrollView在iOS11新增的兩個(gè)屬性:adjustContentInsetcontentInsetAdjustmentBehavior

adjustContentInset表示contentView.frame.origin偏移了scrollview.frame.origin多少;是系統(tǒng)計(jì)算得來的,計(jì)算方式由contentInsetAdjustmentBehavior決定。有以下幾種枚舉計(jì)算方式:

  1. UIScrollViewContentInsetAdjustmentAutomatic:如果scrollview在一個(gè)automaticallyAdjustsScrollViewContentInset = YES 的controller上,并且這個(gè)Controller包含在一個(gè)Navigation controller中,這種情況下會(huì)設(shè)置在top & bottom上 adjustedContentInset = safeAreaInset + contentInset不管是否滾動(dòng)。其他情況下與UIScrollViewContentInsetAdjustmentScrollableAxes相同

  2. UIScrollViewContentInsetAdjustmentScrollableAxes: 在可滾動(dòng)方向上adjustedContentInset = safeAreaInset + contentInset,在不可滾動(dòng)方向上adjustedContentInset = contentInset;依賴于scrollEnabled和alwaysBounceHorizontal / Vertical = YES,scrollEnabled默認(rèn)為YES,所以大多數(shù)情況下,計(jì)算方式還是adjustedContentInset = safeAreaInset + contentInset

  3. UIScrollViewContentInsetAdjustmentNever: 這種方式下adjustedContentInset = contentInset

  4. UIScrollViewContentInsetAdjustmentAlways: 這種方式下會(huì)這么計(jì)算 adjustedContentInset = safeAreaInset + contentInset

當(dāng)contentInsetAdjustmentBehavior設(shè)置為UIScrollViewContentInsetAdjustmentNever的時(shí)候,adjustContentInset值不受SafeAreaInset值的影響。

解決辦法總結(jié)

重新設(shè)置tableView的contentInset值,來抵消掉SafeAreaInset值

因?yàn)閮?nèi)容下移偏移量 = contentInset + SafeAreaInset,如果之前自己設(shè)置了contentInset值為(64,0,0,0),現(xiàn)在系統(tǒng)又設(shè)置了SafeAreaInsets值為(64,0,0,0),那么tableView內(nèi)容下移了64pt,這種情況下,可以設(shè)置contentInset值為(0,0,0,0),也就是遵從系統(tǒng)的設(shè)置了。

設(shè)置tableView的contentInsetAdjustmentBehavior屬性

contentInsetAdjustmentBehavior屬性也是用來取代automaticallyAdjustsScrollViewInsets屬性的,推薦使用這種方式
如果不需要系統(tǒng)為你設(shè)置邊緣距離,可以做以下設(shè)置:

//如果iOS的系統(tǒng)是11.0,會(huì)有這樣一個(gè)宏定義“#define __IPHONE_11_0  110000”;如果系統(tǒng)版本低于11.0則沒有這個(gè)宏定義
#ifdef __IPHONE_11_0   
if ([tableView respondsToSelector:@selector(setContentInsetAdjustmentBehavior:)]) {
    tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
#endif

通過設(shè)置iOS 11新增的屬性addtionalSafeAreaInset

iOS 11之前,通過將Controller的automaticallyAdjustsScrollViewInsets屬性設(shè)置為NO,來禁止系統(tǒng)對(duì)tableView調(diào)整contentInsets的。如果還是想從Controller級(jí)別解決問題,那么可以通過設(shè)置Controller的additionalSafeAreaInsets屬性,如果SafeAreaInset值為(20,0,0,0),那么設(shè)置additionalSafeAreaInsets屬性值為(-20,0,0,0),則SafeAreaInsets不會(huì)對(duì)adjustedContentInset值產(chǎn)生影響,tableView內(nèi)容不會(huì)顯示異常。這里需要注意的是addtionalSafeAreaInset是Controller的屬性,要知道SafeAreaInset的值是由哪個(gè)Controller引起的,可能是由自己的Controller調(diào)整的,可能是navigationController調(diào)整的。是由哪個(gè)Controller調(diào)整的,則設(shè)置哪個(gè)Controller的addtionalSafeAreaInset值來抵消掉SafeAreaInset值。

導(dǎo)航適配

  • iOS 11增加了大標(biāo)題的顯示,通過UINavigationBar的prefersLargeTitles屬性控制,默認(rèn)是不開啟的??梢院雎圆挥米鲞m配。

可以通過navigationItem的largeTitleDisplayMode屬性控制不同頁面大標(biāo)題的顯示,枚舉如下:

typedef NS_ENUM(NSInteger, UINavigationItemLargeTitleDisplayMode) {
    // 默認(rèn)自動(dòng)模式依賴上一個(gè) item 的特性
    UINavigationItemLargeTitleDisplayModeAutomatic,
    // 針對(duì)當(dāng)前 item 總是啟用大標(biāo)題特性
    UINavigationItemLargeTitleDisplayModeAlways,
    // Never 
    UINavigationItemLargeTitleDisplayModeNever,
} NS_SWIFT_NAME(UINavigationItem.LargeTitleDisplayMode);
  • Navigation 集成 UISearchController

把你的UISearchController賦值給navigationItem,就可以實(shí)現(xiàn)將UISearchController集成到Navigation。

navigationItem.searchController  //iOS 11 新增屬性
navigationItem.hidesSearchBarWhenScrolling //決定滑動(dòng)的時(shí)候是否隱藏搜索框;iOS 11 新增屬性
  • UINavigationController和滾動(dòng)交互

滾動(dòng)的時(shí)候,以下交互操作都是由UINavigationController負(fù)責(zé)調(diào)動(dòng)的:

  1. UIsearchController搜索框效果更新
  2. 大標(biāo)題效果的控制
  3. Rubber banding效果 //當(dāng)你開始往下拉,大標(biāo)題會(huì)變大來回應(yīng)那個(gè)滾輪

所以,如果你使用navigation bar,組裝push和pop體驗(yàn),你不會(huì)得到searchController的集成、大標(biāo)題的控制更新和Rubber banding效果,因?yàn)檫@些都是由UINavigationController控制的。

  • UIToolbar and UINavigationBar— Layout

在 iOS 11 中,當(dāng)蘋果進(jìn)行所有這些新特性時(shí),也進(jìn)行了其他的優(yōu)化,針對(duì) UIToolbar 和 UINavigaBar 做了新的自動(dòng)布局?jǐn)U展支持,自定義的bar button items、自定義的title都可以通過layout來表示尺寸。 需要注意的是,你的constraints需要在view內(nèi)部設(shè)置,所以如果你有一個(gè)自定義的標(biāo)題視圖,你需要確保任何約束只依賴于標(biāo)題視圖及其任何子視圖。當(dāng)你使用自動(dòng)布局,系統(tǒng)假設(shè)你知道你在做什么。

  • Avoiding Zero-Sized Custom Views

自定義視圖的size為0是因?yàn)槟阌幸恍┠:募s束布局。要避免視圖尺寸為0,可以從以下方面做:

  1. UINavigationBar 和 UIToolbar 提供位置

  2. 開發(fā)者則必須提供視圖的size,有三種方式:

    a. 對(duì)寬度和高度的約束;
    b. 實(shí)現(xiàn) intrinsicContentSize;
    c. 通過約束關(guān)聯(lián)你的子視圖;

導(dǎo)航欄

導(dǎo)航欄高度的變化

iOS11之前導(dǎo)航欄默認(rèn)高度為64pt(statusBar + NavigationBar),iOS11之后如果設(shè)置了prefersLargeTitles = YES則為96pt,默認(rèn)情況下還是64pt,但在iPhoneX上由于劉海的出現(xiàn)statusBar由以前的20pt變成了44pt,所以iPhoneX上高度變?yōu)?8pt,如果項(xiàng)目里隱藏了導(dǎo)航欄加了自定義按鈕之類的,注意適配一下。

導(dǎo)航欄圖層及對(duì)titleView布局的影響

iOS11之前導(dǎo)航欄的title是添加在`UINavigationItemView上面,而navigationBarButton則直接添加在UINavigationBar上面,如果設(shè)置了titleView,則titleView也是直接添加在UINavigationBar上面。


iOS11之后,大概因?yàn)閘argeTitle的原因,視圖層級(jí)發(fā)生了變化,如果沒有給titleView賦值,則titleView會(huì)直接添加在_UINavigationBarContentView上面,如果賦值了titleView,則會(huì)把titleView添加在_UITAMICAdaptorView上,而navigationBarButton被加在了_UIButtonBarStackView上,然后他們都被加在了_UINavigationBarContentView上


如果你的項(xiàng)目是自定義的navigationBar,那么在iOS11上運(yùn)行就可能出現(xiàn)布局錯(cuò)亂的bug,解決辦法是重寫UINavigationBar的layoutSubviews方法,調(diào)整布局:

- (void)layoutSubviews {
    [super layoutSubviews];
    
    // 注意導(dǎo)航欄及狀態(tài)欄高度適配
    self.frame = CGRectMake(0, 0, CGRectGetWidth(self.frame), naviBarHeight);
    for (UIView *view in self.subviews) {
        if([NSStringFromClass([view class]) containsString:@"Background"]) {
            view.frame = self.bounds;
        }else if ([NSStringFromClass([view class]) containsString:@"ContentView"]) {
            CGRect frame = view.frame;
            frame.origin.y = statusBarHeight;
            frame.size.height = self.bounds.size.height - frame.origin.y;
            view.frame = frame;
        }
    }
}

titleView支持autolayout,這要求titleView必須是能夠自撐開的或?qū)崿F(xiàn)了- intrinsicContentSize方法:

- (CGSize)intrinsicContentSize {
    return UILayoutFittingExpandedSize;
}

TabBarController

主要是tabBar高度及tabBarItem偏移適配,iPhoneX由于底部安全區(qū)的原因UITabBar高度由49pt變成了83pt,可以通過判斷機(jī)型來修改相關(guān)界面代碼:

// UIDevice 分類
- (BOOL)isIPhoneX{
    if ([UIScreen instancesRespondToSelector:@selector(currentMode)]) {
        return CGSizeEqualToSize(CGSizeMake(1125, 2436), [[UIScreen mainScreen] currentMode].size);
    }else{
        return NO;
    }
}

其他適配注意事項(xiàng)

部分項(xiàng)目的輪播圖在iOS 11+Xcode編譯的情況下,會(huì)出現(xiàn)是上下左右任意滾動(dòng)的bug,推薦使用 YJBannerView輪播圖,完全適配iOS 11。Github地址:YJBannerView 使用方法:鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 適配iOS11 ??蘋果官方文檔??蘋果官方視頻為了掙錢...哦??不是,為了廣大中國的開發(fā)者,蘋果官方文檔出了好多中文...
    philiha閱讀 1,217評(píng)論 1 50
  • 本文為作者原創(chuàng),未經(jīng)作者允許不得轉(zhuǎn)載。該文同時(shí)發(fā)表在騰訊bugly公眾號(hào):http://mp.weixin.qq....
    sonialiu閱讀 112,543評(píng)論 146 573
  • | 導(dǎo)語 本文主要是對(duì)iOS 11下企鵝 FM APP中tableView內(nèi)容下移20pt或下移64pt的問題適配...
    762683ff5d3d閱讀 1,268評(píng)論 4 2
  • 語:本文主要是對(duì)iOS 11下APP中tableView內(nèi)容下移20pt或下移64pt的問題適配的一個(gè)總結(jié)。內(nèi)容包...
    西門淋雨閱讀 766評(píng)論 0 1
  • 今天感覺有些累了,看看時(shí)間,馬上就要7.4了,如果這篇文字編輯的很長的話,那么我就寫了兩天的時(shí)間,不知道為什么,我...
    深水里的星星閱讀 263評(píng)論 0 0

友情鏈接更多精彩內(nèi)容