前言
前段時(shí)間有個(gè)小伙伴想統(tǒng)一設(shè)置app導(dǎo)航欄返回按鈕的文字,但是用的方法是
[UINavigationBar appearance].backItem.title = @"返回";
來(lái)問(wèn)為什么沒起作用,其實(shí)是概念的不清晰,想當(dāng)然以為backItem是返回按鈕。故從蠻久以前的筆記里整理了這篇文章( [UINavigationBar appearance].backItem其實(shí)是nil )
基礎(chǔ)概念
UINavigationBar管理?xiàng)I纤锌刂破鞯膎avigationItem(UINavigationItem)径密,保存在items屬性中,
topItem是棧頂?shù)膇tem,即對(duì)當(dāng)前頁(yè)面起作用item(當(dāng)前控制器中獲取self.navigationItem)
例:從A控制器push到了B控制器
B.navigationItem == B.navigationController.navigationBar.topItem
backItem是前一個(gè)控制器的navigationItem
即:A.navigationItem == B.navigationController.navigationBar.backItem
UINavigationItem繼承自NSObject,相當(dāng)于一個(gè)模型廷粒,
封裝了當(dāng)前導(dǎo)航bar上需要顯示的所有信息。
(包含title红且,titleView坝茎,backBarButtonItem(UIBarButtonItem),leftBarButtonItem(UIBarButtonItem)暇番,rightBarButtonItem(UIBarButtonItem)等屬性)
UIBarButtonItem是放在bar上的特殊的button嗤放,能處理點(diǎn)擊事件
UINavigationItem對(duì)象可設(shè)置多個(gè)UIBarButtonItem對(duì)象,
例控制器中設(shè)置 :
self.navigationItem.leftBarButtonItem;
self.navigationItem.rightBarButtonItem;
當(dāng)前控制器的navigationItem的backBarButtonItem決定下一個(gè)控制器的返回按鈕壁酬。
即設(shè)置A.navigationItem.backBarButtonItem次酌,實(shí)際上設(shè)置的是在B控制器中顯示默認(rèn)的返回按鈕
設(shè)置導(dǎo)航欄返回按鈕文字:
導(dǎo)航欄左側(cè)按鈕規(guī)則:
例:從A控制器 push到B控制器
- 如果B控制器有自定義的leftBarButtonItem,則顯示這個(gè)leftBarButtonItem;
- 如果B控制器沒有舆乔,看A控制器backBarButtonItem屬性是否有自定義項(xiàng)岳服,有則顯示;
- 12都沒有,則默認(rèn)顯示一個(gè)返回按鈕希俩,文字是A控制器的標(biāo)題吊宋。
方法一:
在B控制器中設(shè)置leftBarButtonItem。
UIBarButtonItem *leftItem = [[UIBarButtonItem alloc]initWithTitle:@"返回" style:UIBarButtonItemStylePlain target:self action:@selector(back)];
self.navigationItem.leftBarButtonItem = leftItem;
注:如果給控制器添加了leftBarButtonItem颜武,系統(tǒng)側(cè)滑返回手勢(shì)會(huì)失效
方法二:
在A控制器中設(shè)置backBarButtonItem璃搜,實(shí)際上設(shè)置的就是B控制器上默認(rèn)顯示的返回按鈕
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"返回" style:UIBarButtonItemStylePlain target:nil action:nil];
**注:self.navigationItem.backBarButtonItem.title = @"返回”; 直接這樣設(shè)置無(wú)效,
title需要item設(shè)置到bar之前修改鳞上,而backBarButtonItem是系統(tǒng)內(nèi)置的摄职,所以直接改無(wú)效曲管,而且如果沒有手動(dòng)設(shè)置backBarButtonItem废登,獲取到的backBarButtonItem也是nil擎宝。
方法三:利用runtime一次設(shè)置全部控制器
1、建一個(gè)UIViewController分類
2、在分類的+load方法里用method swizzling來(lái)hook控制器的viewDidLoad方法
3趾断、同方法二統(tǒng)一設(shè)置
文字設(shè)置為空的做法也可以設(shè)置為創(chuàng)建title是空字符串的UIBarButtonItem
網(wǎng)上也有做法是設(shè)置文字偏移到屏幕外:(據(jù)說(shuō)會(huì)有閃屏的可能拒名?)
[[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(0, -100)
forBarMetrics:UIBarMetricsDefault];
統(tǒng)一設(shè)置導(dǎo)航欄返回按鈕圖片:
UIImage *buttonBackImg = [UIImage imageNamed:@"ic_navbar_back-black_normal”];
// 可以設(shè)置圖片不要渲染
// buttonBackImg = [buttonBackImg imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
[[UINavigationBar appearance] setBackIndicatorImage:buttonBackImg];
[[UINavigationBar appearance] setBackIndicatorTransitionMaskImage:buttonBackImg];
處理設(shè)置了leftBarButtonItem后側(cè)滑返回失效/控制器中scrollview與側(cè)滑共存問(wèn)題:
核心就是自定義一個(gè)導(dǎo)航控制器,并作為手勢(shì)對(duì)象的代理和導(dǎo)航控制器的代理芋酌,同時(shí)處理手勢(shì)沖突問(wèn)題
#import “NavViewController.h"
@interface NavViewController ()<UINavigationControllerDelegate,UIGestureRecognizerDelegate>
@end
@implementation NavViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
__weak __typeof(self) weakSelf = self;
if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)])
{
// UIScreenEdgePanGestureRecognizer
self.interactivePopGestureRecognizer.delegate = weakSelf;
self.delegate = weakSelf;
}
}
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)])
self.interactivePopGestureRecognizer.enabled = NO;
[super pushViewController:viewController animated:animated];
}
#pragma mark UINavigationControllerDelegate
- (void)navigationController:(UINavigationController *)navigationController
didShowViewController:(UIViewController *)viewController
animated:(BOOL)animate
{
// Enable the gesture again once the new controller is shown
if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)])
self.interactivePopGestureRecognizer.enabled = YES;
}
#pragma mark UINavigationControllerDelegate
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
if ([self.childViewControllers count] == 1) {
return NO;
}
return YES;
}
// ViewController接受多個(gè)手勢(shì)增显,再處理手勢(shì)沖突問(wèn)題。
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
// 處理手指在滑動(dòng)的時(shí)候脐帝,被pop的 ViewController 中的 UIScrollView 會(huì)跟著一起滾動(dòng)問(wèn)題
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return [gestureRecognizer isKindOfClass:UIScreenEdgePanGestureRecognizer.class];
}
@end
導(dǎo)航欄自定義
// 底部陰影
[[UINavigationBar appearance] setShadowImage:[[UIImage alloc] init]];
// 背景圖片
[[UINavigationBar appearance] setBackgroundImage:[[UIImage alloc] init] forBarMetrics:UIBarMetricsDefault];
//bar背景顏色
[[UINavigationBar appearance] setBarTintColor:[UIColor clearColor]];
// 文字顏色
[[UINavigationBar appearance] setTintColor:[UIColor whiteColor]];
// 標(biāo)題位置偏移
[[UINavigationBar appearance] setTitleVerticalPositionAdjustment:10 forBarMetrics:UIBarMetricsDefault];
// 標(biāo)題字體
NSShadow *shadow = [[NSShadow alloc] init];
shadow.shadowColor = [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.8];
shadow.shadowOffset = CGSizeMake(0, 0);
[[UINavigationBar appearance] setTitleTextAttributes: [NSDictionary dictionaryWithObjectsAndKeys:
[UIFont systemFontOfSize:16], NSFontAttributeName,
shadow,NSShadowAttributeName, nil]];
// 返回圖標(biāo)
[UINavigationBar appearance].backIndicatorImage =
[UINavigationBar appearance].backIndicatorTransitionMaskImage =
[[UIImage imageNamed:@"ic_navbar_back-black_normal"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];