一、UINavigationBar的結構
導航欄幾乎是每個頁面都會碰到的問題,一般兩種處理方式:1.隱藏掉不顯示 2.自定義
1. 添加導航欄
TestViewController * mainVC = [[TestViewController alloc] init];
UINavigationController * nav = [[UINavigationController alloc] initWithRootViewController:mainVC]; self.window.rootViewController = nav;
2. 隱藏導航欄
在的TestViewController.m文件中,使用以下代碼:
- (void)viewDidLoad { [super viewDidLoad]; [self.navigationController setNavigationBarHidden:YES]; //self.navigationController.navigationBar.hidden = YES; }
說明:通過屬性直接設置之所以能成功,是因為雖然navigationBar是readonly,但是hidden是默認的(readwrite)。建議使用第一個,通過發送消息來設置。
3. 修改導航欄背景色
由於系統自帶的導航欄已經不能滿足用戶的審美需求,因此開發中導航欄或多或少都被自定義了;即便只是修改了背景色或是字體顏色。
- 方式一:通過代理來設置
[[UINavigationBar appearance] setBarTintColor:[UIColor redColor]];
效果圖
說明:為什么能修改所有導航欄的背景色?
- 查看UINavigationBar發送的消息appearance,是一個名為UIAppearance協議。根據官網解釋:UIAppearance是一個外觀協議(裝飾模式)。要想改變外觀需要實現此協議,因此UINavigationBar內部肯定實現了此協議。
- 查看頭文件UIAppearance協議里面有4個方法,但是根據官網文檔,iOS9以后有兩個方法被廢棄。因此只有下面兩個可用,都返回的是類的對象,因此可以給此類發送修改外觀的消息.比如:setBarTintColor。其實這里拿到對象后可以任意修改。
+ (instancetype)appearance;
+ (instancetype)appearanceForTraitCollection:(UITraitCollection *)trait
- 方式二、直接通過屬性設置,或發送消息來設置
在的TestViewController.m文件中,使用以下代碼:
- (void)viewDidLoad { [super viewDidLoad]; // 無效果 self.navigationController.navigationBar.backgroundColor = [UIColor redColor]; // 可行 [self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"nav.png"] forBarMetrics:UIBarMetricsDefault]; }
說明:
- 下面代碼為什么無效果,其實看一下結構圖就明白了。
self.navigationController.navigationBar.backgroundColor = [UIColor redColor];

沒錯:這樣修改的是navigationBar的背景色,而navigationBar里面添加了UIView和UIImageView,UILabel等控件,覆蓋了navigationBar。
- 那么如何來修改?
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"yao_1"] forBarMetrics:UIBarMetricsDefault];
通過添加背景圖,使用默認的模式。
注意:這里會出現狀態欄背景色也變化的問題,其實這是由於在navigationBar添加的控件自動延伸到邊界造成的。如果只是修改導航欄的背景色,完全沒必要再給navigationBar添加控件了,給navigationBar堆太多的無用控件,不能忍。
// 設置透明 [self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"yao_1"] forBarMetrics:UIBarMetricsCompact]; // 修改navigationBar的背景色 self.navigationController.navigationBar.backgroundColor = [UIColor redColor];
這樣修改navigationBar背景色,才有種很舒暢的感覺,有木有。因為我就只是要修改navigationBar的背景色而已么。效果圖:

為什么要這樣
[[UINavigationBar appearance] setBarTintColor:[UIColor redColor]];
或者為什么要這樣
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"yao_1"] forBarMetrics:UIBarMetricsDefault];
UINavigationBar和UINavigationController、UIViewController關系
1.導航欄類名:UINavigationBar,是導航控制器(UINavgationController)下得一個屬性,一般給一個視圖控制器(TestViewController)添加導航欄,也就是把它添加為導航控制器(UINavgationController)的根視圖控制器。
2.在此視圖控制器中為什么可以直接操作導航控制器,因為TestViewController類是自定義的繼承自UIViewController類的。而UIViewController類中有一個類別,聲明了UINavgationController類的一個屬性。在第一步的時候,把這個屬性設置為上面導航控制器的對象。

3.UINavigationBar對照圖如下:

UINavgationBar結構圖說明:
- 從圖中的對照關系可以看到,UINavigationBar包含一個:UINavigationBarBackground控件(已經延伸到邊界,覆蓋掉了導航欄),另一個UINavigationBarBackIndicatorView(其實就是那個藍色的返回按鈕,當push進來一個ViewController才會顯示)
- UINavigationBarBackground控件里面包含:UIBackdropView(UIBackdropEffectView標識陰影視圖),UIImageView
總結:
可以看到,UINavigationBarBackIndicatorView(返回按鈕只占了左邊一部分區域,並且在push新的視圖控制器時才顯示。因此,顯示在最上面的是UIImageView)。所以,這才是通過代碼
self.navigationController.navigationBar.backgroundColor = [UIColor redColor];
*修改背景色,老是被遮住的根本原因。解決方法,因此掉UIImageView控件,或者干脆給UIImageView設置一張你想要的背景色的圖片。*
二、UINavigationBar的自定義
1. 自定義
一般自定義分三種情況:左側、右側和中間視圖
- 自定義左側按鈕
- (void)viewDidLoad { [super viewDidLoad]; // 自定義導航欄左側按鈕 UIButton * leftBtn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; leftBtn.frame = CGRectMake(0, 7, 83, 30); leftBtn.backgroundColor = [UIColor orangeColor]; [leftBtn addTarget:self action:@selector(onTap) forControlEvents:UIControlEventTouchUpInside]; UIBarButtonItem * leftItem = [[UIBarButtonItem alloc] initWithCustomView:leftBtn]; self.navigationItem.leftBarButtonItem = leftItem; } // 點擊事件處理 - (void)onTap { NSLog(@"點擊了導航欄左側按鈕"); }
-
自定義右側按鈕
和自定義左側按鈕方法類似,差別如下:
self.navigationItem.rightBarButtonItem = rightItem;
-
自定義中間視圖
UIView * centerView = [[UIView alloc] initWithFrame:CGRectMake(0, 7, 110, 30)]; centerView.backgroundColor = [UIColor greenColor]; self.navigationItem.titleView = centerView;
2. 結構分析
從自定義的代碼看到,自定義都是在修改navigationItem里面的東西。查看頭文件可知,TestViewController之所以可以操作navigationItem,和上面UINavigationController實現方式一樣。
UINavigationBar類中有一個items集合,存儲navigationItem和其他相關內容。UINavigationItem類里面存儲leftBarButtonItems、rightBarButtonItems等集合。同時也會有leftBarButtonItem、rightBarButtonItem控件,根據集合是否為空來決定是否創建和顯示這些控件。
因此,當不設置內容時,默認集合為空,所有控件也就不顯示。
3. 手寫代碼技巧
對於導航欄的自定義,大部分app都是左側為"返回",右側為"地圖",“掃碼”等按鈕,中間除顯示標題外,可能會自定義個分段按鈕,選擇菜單等。而對於這些自定義的按鈕和視圖,尺寸都是根據圖片尺寸和導航欄的寬高憑感覺設置的(總之,我以前是這樣>.<)。
今天在測試iOS7的布局使用了SB,發現控件拖上去它會自適應系統給定的尺寸。然后我綁定控件獲取了這些控件的尺寸。測試了iPhone4s和iPhone6,獲取到的左側和右側按鈕尺寸:frame = {0,7,83,30},中間的視圖尺寸:frame = {0,7,110,30}。
這樣,以后導航欄按鈕沒有什么特殊要求,就可以都按這個尺寸設置了。