002 iOS 11 Safe Area(安全距離) 是個什么屎

  • 檢索詞條:safeAreaInsets iOS 11 安全距離 適配iPhoneX viewSafeAreaInsetsDidChange iOS 11安全距離什么時候被改變

  • 環(huán)境 iOS 11 Xcode9.0 以上(這里也會介紹如果你和你的同事兩個人的Xcode一個是9.0以上 一個是9.0以下情況的解決辦法)

  • 上一篇中我們講了safeAreaInsets的改變時機(jī) 及在不同機(jī)型上的變化 這里我們通過不同的示例來展示不同情況下的安全距離問題

看這個例子: ** 拖兩個自定義的 View, 這個 View 上有一個 顯示很多字的Label。然后設(shè)置這兩個 View 的約束分別是:

1.不考慮安全距離

// 不考慮安全距離
    lable_1.frame = CGRectMake(0, 0, SCREEN_WIDTH, lable_1.height);
    lable_2.frame = CGRectMake(0, SCREEN_HEIGHT - lable_2.height,SCREEN_WIDTH, lable_2.height);
Snip20171104_9.png

可以看出來, 子視圖被頂部的劉海以及底部的 home 指示區(qū)擋住了竹伸。我們可以使用 frame 布局或者 auto layout 來優(yōu)化這個地方:

解決方案
  • 方案一 在viewSafeAreaInsetsDidChange或之后的方法中布局這連個lable
- (void)viewSafeAreaInsetsDidChange{
    [super viewSafeAreaInsetsDidChange];
    
    // 考慮安全距離
    UIEdgeInsets insets = self.view.safeAreaInsets;
    _lable_1.frame = CGRectMake(insets.left, self.view.safeAreaInsets.top, SCREEN_WIDTH - insets.left - insets.right, _lable_1.height);
    _lable_2.frame = CGRectMake(insets.left, SCREEN_HEIGHT - _lable_2.height - insets.bottom,SCREEN_WIDTH - insets.left - insets.right, _lable_2.height);
    
    NSLog(@"viewSafeAreaInsetsDidChange__self.view.safeAreaInsets = %@",NSStringFromUIEdgeInsets(self.view.safeAreaInsets));
    
}

Snip20171104_11.png
  • 方案二 直接在自定義的 View 中修改 Label 的布局:
    safeAreaInsetsDidChange
- (void)safeAreaInsetsDidChange{
     NSLog(@"layoutSubviews__self.safeAreaLayoutGuide.layoutFrame = %@",NSStringFromCGRect(self.safeAreaLayoutGuide.layoutFrame));
    
    CGFloat safeTop = self.safeAreaLayoutGuide.layoutFrame.origin.y;
    CGFloat safeBottom = SCREEN_HEIGHT - self.safeAreaLayoutGuide.layoutFrame.size.height - safeTop;
    
    
    _lable_1.frame = CGRectMake(0, safeTop, SCREEN_WIDTH, _lable_1.height);
    
}

Snip20171104_13.png

這樣, 不僅僅是在 ViewController 中能夠使用 safe area 了间景。

UIViewController 中的 safe area
  • 在 iOS 11 中 UIViewController 有一個新的屬性
@property(nonatomic) UIEdgeInsets additionalSafeAreaInsets API_AVAILABLE(ios(11.0), tvos(11.0));

當(dāng) view controller 的子視圖覆蓋了嵌入的子 view controller 的視圖的時候统倒。比如說不跟, 當(dāng) UINavigationController 和 UITabbarController 中的 bar 是半透明(translucent) 狀態(tài)的時候, 就有 additionalSafeAreaInsets

Snip20171104_15.png
- (void)viewSafeAreaInsetsDidChange NS_REQUIRES_SUPER API_AVAILABLE(ios(11.0), tvos(11.0));

- (void)safeAreaInsetsDidChange API_AVAILABLE(ios(11.0),tvos(11.0));

這兩個方法分別是 UIView 和 UIViewController 的 safe area insets 發(fā)生改變時調(diào)用的方法甥温,如果需要做一些處理刷袍,可以重寫這個方法翩隧。有點(diǎn)類似于 KVO 的意思。

模擬 iPhone X 的 safe area
//豎屏
additionalSafeAreaInsets.top = 24.0
additionalSafeAreaInsets.bottom = 34.0
//豎屏, status bar 隱藏
additionalSafeAreaInsets.top = 44.0
additionalSafeAreaInsets.bottom = 34.0
//橫屏
additionalSafeAreaInsets.left = 44.0
additionalSafeAreaInsets.bottom = 21.0
additionalSafeAreaInsets.right = 44.0
UIScrollView 中的 safe area

iOS 7 中引入 UIViewController 的 automaticallyAdjustsScrollViewInsets 屬性在 iOS11 中被廢棄掉了呻纹。取而代之的是 UIScrollView 的 contentInsetAdjustmentBehavior

@property(nonatomic) UIScrollViewContentInsetAdjustmentBehavior contentInsetAdjustmentBehavior API_AVAILABLE(ios(11.0),tvos(11.0));

這是一個枚舉

UIScrollViewContentInsetAdjustmentBehavior
typedef NS_ENUM(NSInteger, UIScrollViewContentInsetAdjustmentBehavior) {
    UIScrollViewContentInsetAdjustmentAutomatic, // 默認(rèn)值
    UIScrollViewContentInsetAdjustmentScrollableAxes, // 
    UIScrollViewContentInsetAdjustmentNever, // 
    UIScrollViewContentInsetAdjustmentAlways, // 
} API_AVAILABLE(ios(11.0),tvos(11.0));
  • never 不做調(diào)整堆生。
  • scrollableAxes content insets 只會針對 scrollview 滾動方向做調(diào)整。
  • always content insets 會針對兩個方向都做調(diào)整雷酪。
  • automatic 這是默認(rèn)值淑仆。當(dāng)下面的條件滿足時, 它跟 always 是一個意思
    能夠水平滾動哥力,不能垂直滾動
    scroll view 是 當(dāng)前 view controller 的第一個視圖
    這個controller 是被navigation controller 或者 tab bar controller 管理的
    automaticallyAdjustsScrollViewInsets 為 true
    在其他情況下 automoatc 跟 scrollableAxes 一樣
Adjusted Content Insets

iOS 11 中 UIScrollView 新加了一個屬性:adjustedContentInset

@property(nonatomic, readonly) UIEdgeInsets adjustedContentInset API_AVAILABLE(ios(11.0),tvos(11.0));
  • adjustedContentInset 和 contentInset 之間有什么區(qū)別呢蔗怠?
    在同時有 navigation 和 tab bar 的 view controller 中添加一個 scrollview 然后分別打印兩個值:
//iOS 10
//contentInset = UIEdgeInsets(top: 64.0, left: 0.0, bottom: 49.0, right: 0.0)
//iOS 11
//contentInset = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0)
//adjustedContentInset = UIEdgeInsets(top: 64.0, left: 0.0, bottom: 49.0, right: 0.0)

然后再設(shè)置:

// 給 scroll view 的四個方向都加 10 的間距
scrollView.contentInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)

打印:

//iOS 10
//contentInset = UIEdgeInsets(top: 74.0, left: 10.0, bottom: 59.0, right: 10.0)
//iOS 11
//contentInset = UIEdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0)
//adjustedContentInset = UIEdgeInsets(top: 74.0, left: 10.0, bottom: 59.0, right: 10.0)

由此可見,在 iOS 11 中 scroll view 實(shí)際的 content inset 可以通過 adjustedContentInset 獲取吩跋。這就是說如果你要適配 iOS 10 的話寞射。這一部分的邏輯是不一樣的。

系統(tǒng)還提供了兩個方法來監(jiān)聽這個屬性的改變

UIScrollView

- (void)adjustedContentInsetDidChange API_AVAILABLE(ios(11.0),tvos(11.0)) NS_REQUIRES_SUPER;

代理:

@protocol UIScrollViewDelegate<NSObject>
- (void)scrollViewDidChangeAdjustedContentInset:(UIScrollView *)scrollView API_AVAILABLE(ios(11.0), tvos(11.0));
UITableView 中的 safe area

Snip20171106_7.png

自定義的 header 上面有一個 lable锌钮,自定義的 cell 上面也有一個 label桥温。將屏幕橫屏之后會發(fā)現(xiàn),cell 以及 header 的布局均自動留出了 safe area 以外的距離梁丘。cell 還是那么大侵浸,只是 cell 的 contnt view 留出了相應(yīng)的距離。這其實(shí)是 UITableView 中新引入的屬性管理的:insetsContentViewsToSafeArea
insetsContentViewsToSafeArea 的默認(rèn)值是 true氛谜, 將其設(shè)置成 no 之后:
Snip20171106_8.png

UICollectionView 中的 safe area

我們在做一個相同的 collection view 來看一下 collection view 中是什么情況:

Snip20171106_9.png

這是一個使用了 UICollectionViewFlowLayout 的 collection view通惫。 滑動方向是豎向的。cell 透明混蔼, cell 的 content view 是白色的履腋。這些都跟上面 table view 一樣。header(UICollectionReusableView) 沒有 content view 的概念, 所以給其自身設(shè)置了紅色的背景。
從截圖上可以看出來遵湖, collection view 并沒有默認(rèn)給 header cell footer 添加safe area 的間距悔政。能夠?qū)⒉季终{(diào)整到合適的情況的方法只有將 header/ footer / cell 的子視圖跟其 safe area 關(guān)聯(lián)起來。


Snip20171106_10.png
 現(xiàn)在我們再試試把布局調(diào)整成更像 collection view 那樣:

1508844448536015.jpg

截圖上可以看出來橫屏下, 左右兩邊的 cell 都被劉海擋住了延旧。這種情況下, 我們可以通過修改 section insets 來適配 safe area 來解決這個問題谋国。但是再 iOS 11 中, UICollectionViewFlowLayout 提供了一個新的屬性 sectionInsetReference 來幫你做這件事情迁沫。

@property (nonatomic) UICollectionViewFlowLayoutSectionInsetReference sectionInsetReference API_AVAILABLE(ios(11.0), tvos(11.0)) API_UNAVAILABLE(watchos);
typedef NS_ENUM(NSInteger, UICollectionViewFlowLayoutSectionInsetReference) {
    UICollectionViewFlowLayoutSectionInsetFromContentInset,
    UICollectionViewFlowLayoutSectionInsetFromSafeArea,
    UICollectionViewFlowLayoutSectionInsetFromLayoutMargins
} API_AVAILABLE(ios(11.0), tvos(11.0)) API_UNAVAILABLE(watchos);

可以看出來芦瘾,系統(tǒng)默認(rèn)是使用 .fromContentInset 我們再分別修改, 看具體會是什么樣子的。

Snip20171106_11.png

這種情況下 section content insets 等于原來的大小加上 safe area insets 的大小集畅。
跟使用 .fromLayoutMargins 相似使用這個屬性 colection view 的 layout margins 會被添加到 section content insets 上面近弟。

Snip20171106_12.png

總結(jié)

 1.在適配 iPhone X 的時候首先是要理解 safe area 是怎么回事。盲目的 if iPhoneX{} 只會給之后的工作代碼更多的麻煩挺智。
       2.如果只需要適配到 iOS9 之前的 storyboard 都只需要做一件事情祷愉。
       3.Xcode9 用 IB 可以看得出來, safe area 到處都是了。理解起來很簡單赦颇。就是系統(tǒng)對每個 View 都添加了 safe area二鳄, 這個區(qū)域的大小,是否跟 view 的大小相同是系統(tǒng)來決定的媒怯。在這個 View 上的布局只需要相對于 safe area 就可以了订讼。每個 View 的 safe area 都可以通過 iOS 11 新增的 API safeAreaInsets 或者 safeAreaLayoutGuide 獲取。
       4.對與 UIViewController 來說新增了 additionalSafeAreaInsets 這個屬性, 用來管理有 tabbar 或者 navigation bar 的情況下額外的情況扇苞。
       5.對于 UIScrollView躯嫉, UITableView, UICollectionView 這三個控件來說杨拐,系統(tǒng)以及做了大多數(shù)的事情。
              scrollView 只需要設(shè)置 contentInsetAdjustmentBehavior 就可以很容易的適配帶 iPhoneX
              tableView 只需要在 cell header footer 等設(shè)置約束的時候相對于 safe area 來做
           對 collection view 來說修改 sectionInsetReference 為 .safeArea 就可以做大多數(shù)的事情了擂啥。
       6.總的來說哄陶, safe area 可以看作是系統(tǒng)在所有的 view 上加了一個虛擬的 view, 這個虛擬的 view 的大小等都是跟 view 的位置等有關(guān)的(當(dāng)然是在 iPhoneX上才有值) 以后在寫代碼的時候哺壶,自定義的控件都盡量針對 safe area 這個虛擬的 view 進(jìn)行布局屋吨。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市山宾,隨后出現(xiàn)的幾起案子至扰,更是在濱河造成了極大的恐慌,老刑警劉巖资锰,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件敢课,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)直秆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門濒募,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人圾结,你說我怎么就攤上這事瑰剃。” “怎么了筝野?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵晌姚,是天一觀的道長。 經(jīng)常有香客問我歇竟,道長挥唠,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任途蒋,我火速辦了婚禮猛遍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘号坡。我一直安慰自己懊烤,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布宽堆。 她就那樣靜靜地躺著腌紧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪畜隶。 梳的紋絲不亂的頭發(fā)上壁肋,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機(jī)與錄音籽慢,去河邊找鬼浸遗。 笑死,一個胖子當(dāng)著我的面吹牛箱亿,可吹牛的內(nèi)容都是我干的跛锌。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼届惋,長吁一口氣:“原來是場噩夢啊……” “哼髓帽!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起脑豹,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤郑藏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后瘩欺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體必盖,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拌牲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了筑悴。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片们拙。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖阁吝,靈堂內(nèi)的尸體忽然破棺而出砚婆,到底是詐尸還是另有隱情,我是刑警寧澤突勇,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布装盯,位于F島的核電站,受9級特大地震影響甲馋,放射性物質(zhì)發(fā)生泄漏埂奈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一定躏、第九天 我趴在偏房一處隱蔽的房頂上張望账磺。 院中可真熱鬧,春花似錦痊远、人聲如沸垮抗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽冒版。三九已至,卻和暖如春逞姿,著一層夾襖步出監(jiān)牢的瞬間辞嗡,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工滞造, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留续室,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓谒养,卻偏偏與公主長得像挺狰,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蝴光,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

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

  • iOS 7 之后蘋果給 UIViewController 引入了 topLayoutGuide 和 bottomL...
    IOSCoderYummy閱讀 774評論 0 0
  • iOS 7 之后蘋果給 UIViewController 引入了 topLayoutGuide 和 bottomL...
    CepheusSun閱讀 35,996評論 45 160
  • 9月份,給自己一個目標(biāo)达址,一定要認(rèn)真學(xué)習(xí)蔑祟,努力改變自己。 還有兩天兒子就要開學(xué)了沉唠,這幾天有點(diǎn)緊迫感疆虚,補(bǔ)起了作業(yè)。 感...
    幸福一定來閱讀 239評論 0 0
  • 小學(xué)時,我的夢想是將來長大了當(dāng)鎮(zhèn)長径簿,管一官河水的污染罢屈。 初中時,我的夢想是做個設(shè)計師篇亭,做出美麗的衣裳缠捌。 后來啊,上...
    陳默梓閱讀 512評論 0 1
  • 【丁隱篇】 丁隱最讓人印象深刻的造型译蒂,就是長老和血魔
    上色請等一下閱讀 216評論 0 1