iOS11 和 iPhone X 適配

Table of Contents

  • iOS11 適配
    • 一、Large Title View
    • 二、導(dǎo)航欄
      • 1. 圖層變化
      • 2. 邊距變化
      • 3.App 需要實(shí)現(xiàn)導(dǎo)航欄左右按鈕邊距為 0
        • 修改思路
      • 4.Avoiding Zero-Sized Custom Views
      • 5.Navigation 集成 UISearchController
    • 三、ScrollView TableView CollectionView
      • 1、TableView Self-Sizing
  • iPhone X 適配
    • 一、屏幕尺寸
      • 1、Human Interface Guidelines
      • 2、布局
    • 二、Bar 高度
      • 1、Status Bar
      • 2、屏幕底部
    • 三、LaunchScreen

一、Large Title View

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

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)?88pt,如果項(xiàng)目里隱藏了導(dǎo)航欄加了自定義按鈕之類(lèi)的,這里需要注意適配一下。

image
image

二、導(dǎo)航欄

1. 圖層變化

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

image

iOS11 之后:
navigationBar 會(huì)添加在_UIButtonBarStackView 上面
_UIButtonBarStackView 則添加在_UINavigationBarContentView 上面;
如果沒(méi)有給 titleView 賦值,則 titleView 會(huì)直接添加在_UINavigationBarContentView 上面
如果賦值給了 titleView,則會(huì)新生成_UITAMICAdaptorView,把 titleView 添加在這個(gè)類(lèi)上面,這個(gè)類(lèi)會(huì)添加在_UINavigationBarContentView 上面

image

2. 邊距變化

在 iOS11 對(duì)導(dǎo)航欄里面的 item 的邊距也做了調(diào)整:

  1. 如果只是設(shè)置了 titleView,沒(méi)有設(shè)置 barButtonItem,把 titleView 的寬度設(shè)置為屏幕寬度,則 titleView 距離屏幕的邊距,iOS11 之前,在 iPhone6p 上是 20p,在 iPhone6p 之前是 16p;iOS11 之后,在 iPhone6p 上是 12p,在 iPhone6p 之前是 8p。
  • 如果只是設(shè)置了 barButtonItem,沒(méi)有設(shè)置 titleView,則在 iOS11 里,barButtonItem 距離屏幕的邊距是 20p 和 16p;在 iOS11 之前,barButtonItem 距離屏幕的邊距也是 20p 和 16p。
  • 如果同時(shí)設(shè)置了 titleView 和 barButtonItem,則在 iOS11 之前,titleView 和 barButtonItem 之間的間距是 6p,在 iOS11 上 titleView 和 barButtonItem 之間無(wú)間距,如下圖:
image
image

3.App 需要實(shí)現(xiàn)導(dǎo)航欄左右按鈕邊距為 0

在 iOS7 之后, 我們?cè)谠O(shè)置 UINavigationItem 的leftBarButtonItem, rightBarButtonItem的時(shí)候都會(huì)造成位置的偏移,iOS11 之前通過(guò)添加一個(gè)消極的寬度為負(fù)值的 UIBarButtonItem

let negativeSpacer = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
negativeSpacer.width = -16
navigationItem.leftBarButtonItems = [negativeSpacer, backItem]

但是由于 iOS 11 導(dǎo)航欄的圖層變化,這招無(wú)效了

修改思路
  • 放棄 UIBarButtonItem, 放棄 UINavigationBar, 使用自定義視圖代替
  • 在 UINavigationBar 中使用添加視圖的方式, 固定位置固定大小添加按鈕
  • 修改 UIBarButtonItem 圖層結(jié)構(gòu) (刪除圖層, 或者修改約束)

初期想法:遍歷 view 的父視圖, 當(dāng)其是UIStackView的時(shí)候, 我們修改其左右約束, 但是僅僅修改的話會(huì)造成約束沖突, 所以我們還需要提前移除約束沖突的左右約束

更理想的方法:修改layoutMargins屬性

@implementation UINavigationBar (SLFixSpace)
+(void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [self swizzleInstanceMethodWithOriginSel:@selector(layoutSubviews)
                                     swizzledSel:@selector(sl_layoutSubviews)];
    });
}

-(void)sl_layoutSubviews{
    [self sl_layoutSubviews];

    if (deviceVersion >= 11) {
        self.layoutMargins = UIEdgeInsetsZero;
        for (UIView *subview in self.subviews) {
            if ([NSStringFromClass(subview.class) containsString:@"ContentView"]) {
                subview.layoutMargins = UIEdgeInsetsZero;//可修正iOS11之后的偏移
            }
        }
    }
}

@end

public var sl_defultFixSpace: CGFloat = 0
public var sl_disableFixSpace: Bool = false

@available(iOS 11.0, *)
extension UINavigationBar {

    static let sl_initialize: Void = {
        DispatchQueue.once(UUID().uuidString) {
            swizzleMethod(UINavigationBar.self,
                          originalSelector: #selector(UINavigationBar.layoutSubviews),
                          swizzleSelector: #selector(UINavigationBar.sl_layoutSubviews))

        }
    }()

    @objc func sl_layoutSubviews() {
        sl_layoutSubviews()

        if sl_disableFixSpace == false {
            layoutMargins = .zero
            let space = sl_defultFixSpace
            for view in subviews {
                if NSStringFromClass(view.classForCoder).contains("ContentView") {
                    view.layoutMargins = UIEdgeInsetsMake(0, space, 0, space)
                }
            }
        }
    }
}


4.Avoiding Zero-Sized Custom Views

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

  1. UINavigationBar 和 UIToolbar 提供位置

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

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

titleView 約束失效,frame 生效


5.Navigation 集成 UISearchController

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

image
Item Normal Height iPhone X Height
UINavigationBar 64 88 (96 large title)
UIStatusBar 20 44
UITabBar 49 83

三、ScrollView TableView CollectionView

1、TableView Self-Sizing

在 iOS8 引入 Self-Sizing 之后,我們可以通過(guò)實(shí)現(xiàn)estimatedRowHeight相關(guān)的屬性來(lái)展示動(dòng)態(tài)的內(nèi)容,實(shí)現(xiàn)了estimatedRowHeight屬性后,得到的初始 contenSize 是個(gè)估算值,是通過(guò)estimatedRowHeight * cell的個(gè)數(shù)得到的,并不是最終的contenSize,tableView就不會(huì)一次性計(jì)算所有的cell的高度了,只會(huì)計(jì)算當(dāng)前屏幕能夠顯示的 cell 個(gè)數(shù)再加上幾個(gè),滑動(dòng)時(shí),tableView不停地得到新的 cell,更新自己的 contenSize,在滑到最后的時(shí)候,會(huì)得到正確的 contenSize。

Self-Sizing在 iOS11 下是默認(rèn)開(kāi)啟的,Headers, footers, and cells 都默認(rèn)開(kāi)啟Self-Sizing,所有 estimated 高度默認(rèn)值從 iOS11 之前的 0 改變?yōu)?code>UITableViewAutomaticDimension

開(kāi)啟Self-Sizing之后,tableView 是使用estimateRowHeight屬性的,這樣就會(huì)造成 contentSize 和 contentOffset 值的變化,如果是有動(dòng)畫(huà)是觀察這兩個(gè)屬性的變化進(jìn)行的,就會(huì)造成動(dòng)畫(huà)的異常,因?yàn)樵诠浪阈懈邫C(jī)制下,contentSize 的值是一點(diǎn)點(diǎn)地變化更新的,所有 cell 顯示完后才是最終的 contentSize 值。因?yàn)椴粫?huì)緩存正確的行高,tableView reloadData 的時(shí)候,會(huì)重新計(jì)算 contentSize,就有可能會(huì)引起 contentOffset 的變化。

iOS11 下不想使用Self-Sizing的話,可以通過(guò)以下方式關(guān)閉:

self.tableView.estimatedRowHeight = 0;
self.tableView.estimatedSectionHeaderHeight = 0;
self.tableView.estimatedSectionFooterHeight = 0;

// 針對(duì)整個(gè)項(xiàng)目
[UITableView appearance].estimatedRowHeight = 0;
[UITableView appearance].estimatedSectionHeaderHeight = 0;
[UITableView appearance].estimatedSectionFooterHeight = 0;

iPhone X 適配

一、屏幕尺寸

1、Human Interface Guidelines
image
image

iOS Device Compatibility Reference - Displays

下圖是 iPhone X 對(duì)比其他機(jī)型的變化部分。iPhone X 和 iPhone 8 的寬度一致,在垂直方向上多了 145pt,這就意味著首頁(yè)可以展示更多的內(nèi)容,多出來(lái)的這 20% 的垂直空間,也許可以掛上更高價(jià)值的運(yùn)營(yíng)位。

[圖片上傳失敗...(image-9f4dbf-1525541516763)]

iPhone X 和其他設(shè)備的尺寸對(duì)比


2、布局

注意上圖藍(lán)色部分,會(huì)發(fā)現(xiàn)這些都算在了展示內(nèi)容的區(qū)域。所以我們?cè)谠O(shè)計(jì)的時(shí)候,要避免內(nèi)容被圓角、劉海給擋住。Like this:

[圖片上傳失敗...(image-4fb479-1525541516763)]

圖 CGRectMake(0,0,100,100)

iPhone X 的坐標(biāo)系統(tǒng)以及能顯示內(nèi)容的區(qū)域如下圖所示:

[圖片上傳失敗...(image-69b649-1525541516763)]

iPhone X 的顯示區(qū)域


二、Bar 高度

Item Normal Height iPhone X Height
UINavigationBar 64 88 (96 large title)
UIStatusBar 20 44
UITabBar 49 83
  • iPhone X 的底部是預(yù)留給系統(tǒng)功能的一個(gè)區(qū)域 - Home Indicator,這部分的高度是 34pt。
  • iPhone X 的狀態(tài)欄由原來(lái)的 20 變?yōu)榱?44。如果之前在導(dǎo)航的位置設(shè)置了自定義 View,在 iPhone X 上會(huì)出問(wèn)題
1、Status Bar

iPhone X 上的 StatusBar 高度比之前的 iPhone 高一些,也就是說(shuō),我們?nèi)绻麑?xiě)死 20pt 高度的 frame 布局,都要大面積修 (tu) 改 (xue)。在 iPhone X 上,通過(guò)打印 [[UIApplication sharedApplication] statusBarFrame] 可以看到,高度是 44pt。

[UIApplication sharedApplication].statusBarFrame

{{0, 0}, {375, 44}}

iPhone X 的狀態(tài)欄高度

如果你的 App 是隱藏 StatusBar 的,建議重新考慮。iPhone X 為用戶(hù)在垂直空間上提供了更多展示余地,且狀態(tài)欄中也包含了用戶(hù)需要知道的信息,除非能通過(guò)隱藏狀態(tài)欄帶給用戶(hù)額外的價(jià)值,否則蘋(píng)果建議大家將狀態(tài)欄還給用戶(hù)。

另外還有一點(diǎn),用戶(hù)在使用 iPhone X 打電話的時(shí)候,StatusBar 的高度也不會(huì)發(fā)生變化了。


2、屏幕底部

因?yàn)闆](méi)有了 Home 鍵,iPhone X 的底部是預(yù)留給系統(tǒng)功能的一個(gè)區(qū)域 - Home Indicator,這部分的高度是 34pt。

iPhone X 的 Home Indicator 區(qū)域

如果你的底部是 TabBar,那么 Home Indicator 背景會(huì)來(lái)自于 TabBar 背景的延伸,如果我們是一個(gè) feed 流的頁(yè)面,那么底部會(huì)展示 feed 流的局部。

意思是如果有 TabBar,那么那個(gè)區(qū)域會(huì)延展你的 barTintColor;沒(méi)有的話,就顯示透明的(參照 Setting)。之所以這么設(shè)計(jì),是為了讓 indicator 清晰可見(jiàn),告訴用戶(hù)你可以滑動(dòng)這部分區(qū)域。所以蘋(píng)果不建議我們的 UI 元素過(guò)于靠近這部分區(qū)域。

有 TabBar 的 Home Indicator 區(qū)

三、LaunchScreen

  • 如果使用 LaunchScreen.storyboard 作為啟動(dòng)頁(yè),需要調(diào)整下 Top 的約束,以前為 -20 ,改為 -44 ;
image
  • 如果是Images.xcassets,為 iPhone X 添加一個(gè)啟動(dòng)圖,新的啟動(dòng)圖尺寸為 1125 像素 * 2436 像素,458ppi。

    image
最后編輯于
?著作權(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),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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