最近項(xiàng)目里有個(gè)需求和導(dǎo)航欄的樣式定制有關(guān),深入之后發(fā)現(xiàn)之前理解的一些概念有些模糊,剛好趁著這次機(jī)會(huì)全面整理了一下。
從 iOS7 開(kāi)始,蘋(píng)果采用了大量的扁平化和毛玻璃風(fēng)格,剛升級(jí)到 iOS7 之后會(huì)發(fā)現(xiàn)界面的布局多多少少有一些偏差(當(dāng)然現(xiàn)在新建的項(xiàng)目沒(méi)有這方面困擾,不需要經(jīng)歷6到7的適配),適配過(guò)程中會(huì)發(fā)現(xiàn)如下一些屬性,
-edgesForExtendedLayout
-translucent
-extendedLayoutIncludesOpaqueBars
-automaticallyAdjustsScrollViewInsets
根據(jù)字面意思看上去這些屬性很好理解,但是發(fā)現(xiàn)他們組合之后會(huì)有一些不同的表現(xiàn),一些奇怪的問(wèn)題也不知道什么原因?qū)е碌摹2挥脫?dān)心,接下去我會(huì)全面的解析一下這幾個(gè)屬性的含義,保證你再也不怕各種奇怪的導(dǎo)航欄問(wèn)題啦。
edgesForExtendedLayout + translucent
iOS7 以后,edgesForExtendedLayout 的默認(rèn)設(shè)置是 UIRectEdgeAll,translucent 的默認(rèn)值是 true。這種組合會(huì)使 rootView 的布局從(0,0)開(kāi)始,即 view 的內(nèi)容會(huì)被導(dǎo)航欄遮擋住,大多數(shù)情況下將 edgesForExtendedLayout 修改為 UIRectEdgeNone 就能解決布局被遮擋的問(wèn)題。將 translucent 設(shè)置成 false 也會(huì)使 rootView 從導(dǎo)航欄底部開(kāi)始,但是 translucent = false 時(shí)即使將 edgesForExtendedLayout 再改成 UIRectEdgeAll rootView 還是從導(dǎo)航欄底部開(kāi)始布局。如何可以在導(dǎo)航欄不透明的情況下讓 rootView 從(0,0)開(kāi)始布局呢?蘋(píng)果也考慮到了這種需求,提供了 extendedLayoutIncludesOpaqueBars 這個(gè)屬性。
小結(jié):translucent 為 true,rootView 從(0,0)開(kāi)始布局,修改 edgesForExtendedLayout 屬性可以改變布局;translucent 為 false,rootView 從導(dǎo)航欄底部開(kāi)始布局,修改 edgesForExtendedLayout 屬性無(wú)法改變布局。
extendedLayoutIncludesOpaqueBars + translucent
前面我們知道了 translucent 為 false 時(shí),修改 edgesForExtendedLayout 也無(wú)法使 rootView 從(0,0)開(kāi)始布局。蘋(píng)果為此提供了 extendedLayoutIncludesOpaqueBars,字面上理解的意思就是在不透明的導(dǎo)航欄下也全屏顯示。
這里多提一點(diǎn),在 ViewController 的生命周期中有 viewDidLoad,viewWillAppear,viewDidAppear,viewWillDisappear,viewDidDisappear,上述提到的這些屬性需要在 viewDidAppear 之前設(shè)置好,viewDidAppear 可以認(rèn)為系統(tǒng)已經(jīng)根據(jù)配置布局好了,在這里展示給用戶(hù)看。
automaticallyAdjustsScrollViewInsets
automaticallyAdjustsScrollViewInsets 默認(rèn)值是 true,表示在全屏模式下會(huì)自動(dòng)修改第一個(gè)添加到 rootView 的 scrollview 的 contentInset 為(64,0,0,0),這樣 scrollview 就不會(huì)被導(dǎo)航欄遮擋了。
關(guān)于 scrollview 有一個(gè)問(wèn)題比較常見(jiàn),這里解析一下原因。我們經(jīng)常會(huì)這么使用一個(gè) tableView,
-(void)viewDidLoad{
[superviewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor=[UIColorgreenColor];
self.navigationItem.title=@"Master";
self.tableView=[[UITableViewalloc]initWithFrame:self.view.bounds];
_tableView.backgroundColor=[UIColorwhiteColor];
_tableView.delegate=self;
_tableView.dataSource=self;
[self.viewaddSubview:_tableView];
}
這樣在默認(rèn)情況下(translucent = true, edgesForExtendedLayout = UIRectEdgeAll),tableView的顯示沒(méi)有問(wèn)題。但是當(dāng)我們將 edgesForExtendedLayout 設(shè)置成 UIRectEdgeNone 時(shí),當(dāng) tableView 的內(nèi)容比較多時(shí)底部的內(nèi)容反而顯示不下。這就很奇怪了,按照前面的結(jié)論,這時(shí)候 tableView是從導(dǎo)航欄底部開(kāi)始布局的,contentInset 也是(0,0,0,0),怎么底部的內(nèi)容會(huì)被遮擋一部分呢?原因在于self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds];初始化時(shí) rootView 的 frame 還是(0,0,screenWidth,screenHeight),只需要在viewWillLayoutSubviews中重新修改一下 tableview 的 frame 即可,
-(void)viewWillLayoutSubviews
{
[superviewWillLayoutSubviews];
_tableView.frame=self.view.bounds;
}
UINavigationBar 修改背景色
UINavigationBar是UIView的子類(lèi),首先想到的是修改背景色,
self.navigationController.navigationBar.backgroundColor = [UIColor greenColor];

發(fā)現(xiàn)這并不是我們想要的效果,為什么綠色變淡了呢?通過(guò) Xcode 的 ViewDebugging 我們可以看到UINavigationBar內(nèi)部還有一些子視圖,這些子視圖的背景色會(huì)遮擋住我們?cè)O(shè)置的顏色。

查看UINavigationBar的接口我們發(fā)現(xiàn)setBackgroundImage,設(shè)置
[self.navigationController.navigationBarsetBackgroundImage:[UIImageimageWithColor:[UIColorgreenColor]size:CGSizeMake(1,1)]forBarMetrics:UIBarMetricsDefault];
結(jié)果如下

小結(jié):設(shè)置UINavigationBar的 backgroundImage 可以修改導(dǎo)航欄的背景色。
translucent 和 setBackgroundImage
前面提到我們可以通過(guò)修改背景圖片來(lái)修改導(dǎo)航欄的背景色,設(shè)置了背景圖片后在有些頁(yè)面我們會(huì)遇到一些奇怪的問(wèn)題,發(fā)現(xiàn)原來(lái)布局正常的頁(yè)面顯示不對(duì)了,會(huì)多出一部分空白或者被導(dǎo)航欄遮擋住了。
通過(guò)打印出 translucent 的值我們發(fā)現(xiàn)設(shè)置了純色的背景圖后原來(lái)半透明的導(dǎo)航欄變成了不透明的,結(jié)合前面提到的 translucent 對(duì)布局起點(diǎn)的影響,如果頁(yè)面是按照半透明情況,即 rootView 從(0,0)開(kāi)始布局來(lái)設(shè)置子視圖的 frame,那么設(shè)置了純色背景圖后 translucent 變成了 false,即 rootView 從(0,64)開(kāi)始布局。為什么設(shè)置背景圖片會(huì)影響 translucent 呢,通過(guò)查看文檔發(fā)現(xiàn)了如下說(shuō)明,
/*
NewbehavioroniOS7.
DefaultisYES.
YoumayforceanopaquebackgroundbysettingthepropertytoNO.
Ifthenavigationbarhasacustombackgroundimage,thedefaultisinferred
fromthealphavaluesoftheimage—YESifithasanypixelwithalpha
也就是說(shuō)背景圖片如果包含 alpha 的色值,系統(tǒng)會(huì)默認(rèn)將 translucent 設(shè)置為 true,沒(méi)有包含 alpha 色值會(huì)將 translucent 設(shè)置為 false。這下真相大白了,原來(lái)我們前面設(shè)置了純綠色的背景圖片,是不包含 alpha 色值的,即系統(tǒng)默認(rèn)將 translucent 設(shè)置成了 false。但這是針對(duì)沒(méi)有手動(dòng)設(shè)置 translucent 值的情況,如果我們手動(dòng)設(shè)置了 translucent,那么系統(tǒng)就不會(huì)根據(jù)背景圖片的 alpha 來(lái)修改 translucent。
至此,我們了解了蘋(píng)果是如何使用這幾個(gè)屬性的,針對(duì) iOS7 以上,這里做一下總結(jié):
iOS7 以后 translucent 默認(rèn)為 true,rootView 從(0,0)開(kāi)始布局,修改 edgesForExtendedLayout 屬性可以改變布局;
translucent 為 false,rootView 從導(dǎo)航欄底部開(kāi)始布局,修改 edgesForExtendedLayout 屬性無(wú)法改變布局,可以通過(guò)設(shè)置 extendedLayoutIncludesOpaqueBars 從(0,0)開(kāi)始布局;
automaticallyAdjustsScrollViewInsets 默認(rèn)值是 true,表示在全屏模式下會(huì)自動(dòng)修改第一個(gè)添加到 rootView 的 scrollview 的 contentInset 為(64,0,0,0),用來(lái)糾正scrollview在全屏模式下的顯示;
設(shè)置UINavigationBar的背景圖片可以改變導(dǎo)航欄背景色,如果背景圖片包含 alpha 的色值,系統(tǒng)會(huì)默認(rèn)將 translucent 設(shè)置為 true,沒(méi)有包含 alpha 色值會(huì)將 translucent 設(shè)置為 false。但這是針對(duì)沒(méi)有手動(dòng)設(shè)置 translucent 值的情況,如果我們手動(dòng)設(shè)置了 translucent,那么系統(tǒng)就不會(huì)根據(jù)背景圖片的 alpha 來(lái)修改 translucent。