我在 解決navigationBar跳轉(zhuǎn)出現(xiàn)的黑色塊 這篇文章里面介紹了設(shè)置 NavigationBar 透明的方法,但是我們?cè)O(shè)置的頁(yè)面的rootView不是UIScrollView,如果是UIScrollView應(yīng)該會(huì)碰到以下的問(wèn)題。
iPhone4s是IOS8系統(tǒng),iPhoneX是IOS11系統(tǒng)
1. scrollView的內(nèi)容沒(méi)有從屏幕開(kāi)始


2.當(dāng)滾動(dòng)到出現(xiàn)紅色的NavigationBar,push下一個(gè)頁(yè)面,再回來(lái)會(huì)發(fā)現(xiàn)標(biāo)題欄變白色了。
- 備注:這里變成白色是因?yàn)槲沂褂?a target="_blank" rel="nofollow">RTRootNavigationController,如果是使用 UINavigationController,會(huì)變成黑色。


以上我在項(xiàng)目中遇到的問(wèn)題,碰到這兩個(gè)問(wèn)題,我也束手無(wú)策,然后就各種搜索,各種嘗試,最終勉強(qiáng)處理了,但是還是沒(méi)弄懂為什么會(huì)這樣。后來(lái)再繼續(xù)深究,才算找到了規(guī)律。
隱藏頁(yè)面布局的起點(diǎn)位置有下面幾個(gè)屬性
- edgesForExtendedLayout
- translucent
- extendedLayoutIncludesOpaqueBars
- automaticallyAdjustsScrollViewInsets
我們先說(shuō) edgesForExtendedLayout、translucent,下面是這兩個(gè)屬性的官方文檔
/*
New behavior on iOS 7.
Default is YES.
You may force an opaque background by setting the property to NO.
If the navigation bar has a custom background image, the default is inferred
from the alpha values of the image—YES if it has any pixel with alpha < 1.0
If you send setTranslucent:YES to a bar with an opaque custom background image
it will apply a system opacity less than 1.0 to the image.
If you send setTranslucent:NO to a bar with a translucent custom background image
it will provide an opaque background for the image using the bar's barTintColor if defined, or black
for UIBarStyleBlack or white for UIBarStyleDefault if barTintColor is nil.
*/
@property(nonatomic,assign,getter=isTranslucent) BOOL translucent NS_AVAILABLE_IOS(3_0) UI_APPEARANCE_SELECTOR; // Default is NO on iOS 6 and earlier. Always YES if barStyle is set to UIBarStyleBlackTranslucent
@property(nonatomic,assign) UIRectEdge edgesForExtendedLayout NS_AVAILABLE_IOS(7_0); // Defaults to UIRectEdgeAll
- edgesForExtendedLayout 默認(rèn)是UIRectEdgeAll,表示屏幕從(0, 0)左上角開(kāi)始布局;如果設(shè)置UIRectEdgeNone,那么就會(huì)從NavigationBar下開(kāi)始布局
- translucent 默認(rèn)是YES,表示屏幕從(0, 0)左上角開(kāi)始布局;如果設(shè)置NO,那么就會(huì)從NavigationBar下開(kāi)始布局;
- 這兩個(gè)的排列組合,就有一下幾種情況:
- A. edgesForExtendedLayout = UIRectEdgeAll; translucent = YES;
- B. edgesForExtendedLayout = UIRectEdgeAll; translucent = NO;
- C. edgesForExtendedLayout = UIRectEdgeNone; translucent = YES;
- D. edgesForExtendedLayout = UIRectEdgeNone; translucent = NO;




- 上面四種情況分別對(duì)應(yīng)四個(gè)效果圖;我們可以得出
*1. translucent = NO; 或者 edgesForExtendedLayout = UIRectEdgeNone;根視圖會(huì)從NavigationBar下開(kāi)始布局
*2. 如果設(shè)置 translucent = NO; 那么 edgesForExtendedLayout 不起作用
需求 1.rootView是UIScrollView,且 NavigationBar是透明的。
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"UIScrollView+透明NavigationBar";
[self setNavigationBarColor:[UIColor clearColor]];
CGFloat width = [UIScreen mainScreen].bounds.size.width;
CGFloat height = [UIScreen mainScreen].bounds.size.height;
UIScrollView *rootView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, width, height)];
rootView.backgroundColor = [UIColor whiteColor];
rootView.contentSize = CGSizeMake(0, height+10);//界面可以滾動(dòng)
[self.view addSubview:rootView];
UIView *containerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, width, height+10)];
containerView.backgroundColor = [UIColor yellowColor];
[rootView addSubview:containerView];
UIView *view = [[UIView alloc] init];
view.backgroundColor = [UIColor redColor];
view.frame = CGRectMake(0, 0, 100, 100);
[containerView addSubview:view];
}

設(shè)置了透明NavigationBar,我們需要的是界面從屏幕左上角開(kāi)始,而不是NavigationBar下方開(kāi)始。這是因?yàn)?automaticallyAdjustsScrollViewInsets
@property(nonatomic,assign) BOOL automaticallyAdjustsScrollViewInsets API_DEPRECATED("Use UIScrollView's contentInsetAdjustmentBehavior instead", ios(7.0,11.0),tvos(7.0,11.0)); // Defaults to YES
/**
Discussion
The default value of this property is YES, which lets container view controllers know that they should adjust the scroll view insets of this view controller’s view to account for screen areas consumed by a status bar, search bar, navigation bar, toolbar, or tab bar. Set this property to NO if your view controller implementation manages its own scroll view inset adjustments.
**/
/**
會(huì)為self.view的滾動(dòng)視圖做適配,會(huì)為你考慮狀態(tài)欄,搜索欄,導(dǎo)航欄,工具欄或選項(xiàng)卡欄的高度,自動(dòng)調(diào)整;默認(rèn)是true,即自動(dòng)調(diào)整;如果要自己控制,就設(shè)置false; ios7.0-ios11.0
**/
//我們上面的問(wèn)題,在ios7.0-ios11.0,做下面設(shè)置就可以解決了
self.automaticallyAdjustsScrollViewInsets = NO;
在ios11.0之后呢,就要設(shè)置 UIScrollView's contentInsetAdjustmentBehavior
/* Configure the behavior of adjustedContentInset.
Default is UIScrollViewContentInsetAdjustmentAutomatic.
*/
@property(nonatomic) UIScrollViewContentInsetAdjustmentBehavior contentInsetAdjustmentBehavior API_AVAILABLE(ios(11.0),tvos(11.0));
//對(duì)于上面的效果圖出現(xiàn)的情況,完整的解決方案是
if (@available(iOS 11.0, *)) {
self.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
} else {
self.automaticallyAdjustsScrollViewInsets = NO;
}
最后還有一個(gè)屬性 extendedLayoutIncludesOpaqueBars 這個(gè)什么作用呢,大家回頭看看最開(kāi)始說(shuō)的兩個(gè)問(wèn)題中的第二個(gè),NavigationBar的顏色是隨scrollView的滑動(dòng)而改變的,當(dāng)我們停留在“褐色NavigationBar”時(shí),進(jìn)入下一個(gè)頁(yè)面,再回來(lái),會(huì)發(fā)現(xiàn)NavigationBar白色了,而且頁(yè)面也從NavigationBar下開(kāi)始,而不是全屏。
If the navigation bar has a custom background image, the default is inferred from the alpha values of the image—YES if it has any pixel with alpha < 1.0
這是文檔中的描述,如果設(shè)置自定義背景圖片,會(huì)從自定義背景圖片獲取alpha,如果它有任何像素的alpha <1.0 就設(shè)為YES,否則就取反。
備注:如果你手動(dòng)設(shè)置了translucent,那這個(gè)自動(dòng)取值就不會(huì)生效
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageWithColor:color] forBarMetrics:UIBarMetricsDefault];
[self.navigationController.navigationBar setShadowImage:[UIImage new]];
//這是我們?cè)O(shè)置NavigationBar的顏色方法,可以在后面打印translucent的值,做驗(yàn)證
- 當(dāng)我們?cè)O(shè)置“透明”時(shí),translucent=YES;當(dāng)我們?cè)O(shè)置“褐色”,translucent=NO;
我們上面也講過(guò)translucent=NO,就是“組合B”的效果圖,會(huì)出現(xiàn)NavigationBar- 為什么在當(dāng)前頁(yè)面滑動(dòng)沒(méi)有問(wèn)題,要進(jìn)入下一個(gè)頁(yè)面再回來(lái)才會(huì)出現(xiàn)呢?
這是因?yàn)檫@個(gè)設(shè)置需要在viewDidAppear設(shè)置好,我們默認(rèn)設(shè)置的是“透明”即translucent=YES,所以顯示正常;當(dāng)滾動(dòng)界面設(shè)置“褐色”時(shí),translucent=NO,從下一個(gè)界面回來(lái),會(huì)觸發(fā)viewWillAppear,系統(tǒng)會(huì)在根據(jù)translucent=NO調(diào)整界面。
所以我們?cè)O(shè)置上面相關(guān)的屬性,要在loadView,viewDidLoad,viewWillAppear等方法中設(shè)置。
那么要怎么解決上面的問(wèn)題呢?
這就是我們最后一個(gè)屬性 extendedLayoutIncludesOpaqueBars 的作用了。
/**
A Boolean value indicating whether or not the extended layout includes opaque bars.
**/
@property(nonatomic,assign) BOOL extendedLayoutIncludesOpaqueBars NS_AVAILABLE_IOS(7_0); // Defaults to NO, but bars are translucent by default on 7_0.
//不透明bar時(shí),還要不要從屏幕頂部開(kāi)始。默認(rèn)值NO
所以上面的問(wèn)題,我們只需要設(shè)置這個(gè)值為YES,就可以解決了
self.extendedLayoutIncludesOpaqueBars = YES;
- 總結(jié):這四個(gè)屬性覆蓋了NavigationBar的很多情況了,了解了這四個(gè)屬性的用法及作用,基本上可以NavigationBar就不會(huì)再困擾你了。
這里還有一篇文章可以參考,iOS 導(dǎo)航欄的那些事兒,可能這篇文章寫得更好。