現(xiàn)象
在使用iOS導(dǎo)航欄時踩上的一些坑课幕,假設(shè)兩個UIViewController:A厦坛、B,B是A的子級視圖控制器
- 在B上自定義返回鍵backBarButtonItem無效及界面異常
- 修改所有UIViewController導(dǎo)航欄的按鈕
原因及解決方法
- 問題一:在B上自定義返回鍵backBarButtonItem無效及界面異常乍惊,可能原因解決方法如下:
-
修改返回鍵標題無效杜秸,在A中設(shè)置了UIViewController的navigationItem.title,如下圖润绎,不設(shè)置時撬碟,該值默認是nil;如果該A先入棧莉撇,而后B入棧呢蛤,此時棧頂?shù)腂的返回鍵自動被設(shè)置為A的navigationItem.title的值,如果A的title為nil棍郎,則B的返回鍵會使用“Back”其障;
-
重新定義返回鍵無效,如果要修改B中的backBarButtonItem涂佃,可以創(chuàng)建一個新的UIBarButtonItem來替換励翼,直接修改backBarButtonItem的title是無效的
推薦使用- (instancetype)initWithCustomView:(UIView *)customView;來自定義不同的樣子;但是如果只是想去掉backBarButtonItem辜荠,則可以使用一個空的UIBarButtonItem來替換
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStyleDone target:nil action:nil];
補充:如果不修改內(nèi)容汽抚,就是裝飾一下文本,可以使用setTitleTextAttributes:forState:
-
重定義的返回鍵標題重疊侨拦,涉及到leftBarButtonItem殊橙,這個的使用場景有點像淘寶和微信的文章,使用在導(dǎo)航欄左側(cè)需要多個按鈕時,比如‘Back’膨蛮、‘Close’多個并列存在的情況叠纹,也可以用leftBarButtonItem來設(shè)置為一個返回鍵,但是需要設(shè)置backBarButtonItem為空或使用hidesBackButton隱藏 (2017.09.04修改)敞葛,不然會出現(xiàn)兩個標題重疊的情況誉察,leftBarButtonItem的設(shè)置方法參考backBarButtonItem的設(shè)置方法,還可以批量設(shè)置多個惹谐,使用leftBarButtonItems持偏。
- 問題二:修改所有UIViewController導(dǎo)航欄的按鈕
實際運用中,像導(dǎo)航欄這樣幾乎所有UIViewController都會用到的東西基本應(yīng)該在項目開始就準備后氨肌,如果要修改屬性鸿秆,秉持優(yōu)化代碼的精神,也應(yīng)該在一處修改怎囚,然后在所有其他地方都生效卿叽,因此,這里有兩種方法:
-
繼承UIViewController重寫導(dǎo)航欄恳守,繼承UIViewController考婴,自定義一個導(dǎo)航欄,隱藏系統(tǒng)的導(dǎo)航欄催烘,然后在需要導(dǎo)航欄的地方用一句代碼實現(xiàn)創(chuàng)建導(dǎo)航欄沥阱。
如我使用過的一個:QHBasicViewController(來源已不可考)
QHBasicViewController.h
#import <UIKit/UIKit.h>
@interface QHBasicViewController : UIViewController
@property (nonatomic, strong) UILabel* navTitleLabel;///導(dǎo)航欄標題
@property (nonatomic, strong, readonly) UIView *navView;///導(dǎo)航欄,只讀
@property (nonatomic, strong) UIImageView* navBackgroundView;///導(dǎo)航欄背景
@property (nonatomic, strong, readonly) UIView* leftV;///導(dǎo)航欄左側(cè)View,只讀
@property (nonatomic, strong, readonly) UIView *rightV;///導(dǎo)航欄右側(cè)View伊群,只讀
/** 創(chuàng)建導(dǎo)航欄
* @param szTitle 導(dǎo)航欄標題
* @param menuItem block回調(diào)考杉,需要在這里定義導(dǎo)航欄上的各個按鈕
*/
- (void)createNavWithTitle:(NSString *)szTitle createMenuItem:(UIView *(^)(int nIndex))menuItem;
@end
QHBasicViewController.m
#import "QHBasicViewController.h"
#import "HHUtil.h"
#define StatusbarHeight ((isIos7 >= 7 && __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_6_1)?20.f:0.f)
#pragma mark - QHBasicViewController
@interface QHBasicViewController ()
{
}
@end
@implementation QHBasicViewController
- (void)viewWillAppear:(BOOL)animated
{
//隱藏UIViewController的導(dǎo)航欄
[[self navigationController] setNavigationBarHidden:YES];
[super viewWillAppear:TRUE];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
}
- (void)createNavWithTitle:(NSString *)szTitle createMenuItem:(UIView *(^)(int nIndex))menuItem
{
const CGFloat screenWidth = [[UIScreen mainScreen] bounds].size.width;
const CGFloat navigationViewHeigth = 44.f;
const CGFloat _nSpaceNavY = (isIos7 >= 7 && __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_6_1) ? 0 : 20;
UIImageView *navIV = [[UIImageView alloc] initWithFrame:CGRectMake(0, _nSpaceNavY, screenWidth, 64 - _nSpaceNavY)];
[navIV setImage:[UIImage imageNamed:@"bg_nav.png"]];
_navBackgroundView = navIV;
[self.view addSubview:navIV];
/* { 導(dǎo)航條 } */
_navView = [[UIImageView alloc] initWithFrame:CGRectMake(0.f, StatusbarHeight, screenWidth, navigationViewHeigth)];
((UIImageView *)_navView).backgroundColor = [UIColor clearColor];
[self.view addSubview:_navView];
_navView.userInteractionEnabled = YES;
//
if (szTitle != nil)
{
UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake((_navView.bounds.size.width - 240)/2, (_navView.bounds.size.height - 40)/2, 240, 40)];
[titleLabel setText:szTitle];
[titleLabel setTextAlignment:NSTextAlignmentCenter];
[titleLabel setTextColor:[UIColor whiteColor]];
[titleLabel setFont:[UIFont boldSystemFontOfSize:18]];
[titleLabel setBackgroundColor:[UIColor clearColor]];
titleLabel.numberOfLines = 2;
_navTitleLabel = titleLabel;
[_navView addSubview:_navTitleLabel];
}
//
UIView *item1 = menuItem(0);
if (item1 != nil)
{
_leftV = item1;
[_navView addSubview:item1];
}
UIView *item2 = menuItem(1);
if (item2 != nil)
{
_rightV = item2;
[_navView addSubview:item2];
}
}
@end
在創(chuàng)建你自己的UIViewController的時候,需要做的就是繼承QHBasicViewController而不是UIViewController舰始,然后在viewDidLoad的時候createNavWithTitle: createMenuItem:創(chuàng)建導(dǎo)航欄奔则,然后在塊回調(diào)中定義你自己的按鈕。
-
繼承UINavigationController重寫push方法蔽午,繼承UINavigationController方法,重寫push方法酬蹋,在push前后統(tǒng)一修改backBarButtonItem及老、leftBarButtonItem和rightBarButtonItem,甚至其它的導(dǎo)航欄設(shè)置范抓,比如自定義titleView骄恶,導(dǎo)航欄背景之類的。
奉上個人做的一個例子:
HHNavigationController.h
#import <UIKit/UIKit.h>
@interface HHNavigationController : UINavigationController
/** 重載的initWithRootViewController:方法匕垫,附帶初始化導(dǎo)航欄
* @param rootViewController 視圖控制器
*/
- (instancetype)initWithRootViewController:(UIViewController *)rootViewController;
@end
HHNavigationController.m
#import "HHNavigationController.h"
#import <MediaPlayer/MediaPlayer.h>
@interface HHNavigationController ()<UIGestureRecognizerDelegate>
@property (nonatomic, strong) UIBarButtonItem *backBarButtonItm;
@end
@implementation HHNavigationController
- (instancetype)initWithRootViewController:(UIViewController *)rootViewController
{
self = [super initWithRootViewController:rootViewController];
if (self) {
//設(shè)置導(dǎo)航欄和狀態(tài)欄字體顏色為白色僧鲁,導(dǎo)航欄背景設(shè)置為綠色
rootViewController.navigationController.navigationBar.barTintColor = RGBA(129, 164, 128, 1);//導(dǎo)航欄為綠色
rootViewController.navigationController.navigationBar.barStyle = UIBarStyleBlack;//狀態(tài)欄字體為白色
rootViewController.navigationController.navigationBar.translucent = NO;//導(dǎo)航欄不透明
//設(shè)置側(cè)滑滑動返回
self.interactivePopGestureRecognizer.delegate = self;
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - action
-(void)onBack
{
[self popViewControllerAnimated:YES];
}
#pragma mark - back button
-(UIBarButtonItem *)backBarButtonItm
{
if (!_backBarButtonItm) {
UIButton *backBtn = [UIButton buttonWithType:UIButtonTypeCustom];
backBtn.frame = CGRectMake(0, 0, 30, 30);
[backBtn setImage:[UIImage imageNamed:@"btn_back"] forState:UIControlStateNormal];
[backBtn addTarget:self action:@selector(onBack) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *backBarItm = [[UIBarButtonItem alloc] initWithCustomView:backBtn];
_backBarButtonItm = backBarItm;
}
return _backBarButtonItm;
}
#pragma mark - Override
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
viewController.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:nil style:UIBarButtonItemStyleDone target:nil action:nil];//返回鍵置空
[super pushViewController:viewController animated:animated];
if (viewController.navigationItem.leftBarButtonItem == nil && [self.viewControllers count] > 1) {
//設(shè)置返回鍵
viewController.navigationItem.leftBarButtonItem = self.backBarButtonItm;
//設(shè)置導(dǎo)航欄和狀態(tài)欄字體顏色為白色,導(dǎo)航欄背景設(shè)置為綠色
viewController.navigationController.navigationBar.barTintColor = RGBA(129, 164, 128, 1);//導(dǎo)航欄為綠色
viewController.navigationController.navigationBar.barStyle = UIBarStyleBlack;//狀態(tài)欄字體為白色
viewController.navigationController.navigationBar.translucent = NO;//導(dǎo)航欄不透明
}
}
@end
缺點
- 原生的導(dǎo)航欄雖然方便,但是犧牲了很多便利寞秃,自定義起來比較麻煩斟叼,特別是如果要求動畫的話(這個需求很正常),基本就用不了只能自己寫一個偽導(dǎo)航欄來替代
- 不容易統(tǒng)一春寿,如果使用present方法朗涩,呈現(xiàn)的方式都不同,也不會有導(dǎo)航欄绑改,雖然有方法加導(dǎo)航欄谢床,但是還是有差異,也容易給代碼的編寫造成困擾
- 難定制厘线,遇到定制需求识腿,由于backBarButtonItem本身要求的在加載到bar之前設(shè)置好的限制,很難動態(tài)的變化
修改
2017.09.02 16:13 V1.0 Create
2017.09.04 09:36 V1.1 添加backBarButtonItem的隱藏方法介紹