前言
相信大家對iOS的導航欄都會有自己的一點不爽,在項目開發過程中總是會出現這樣那樣莫名其妙的問題,而且對系統API提供的一些接口并不能達到很好的效果,一個小小的返回按鈕就把我們整的要命。網上對于返回按鈕的處理基本包括兩種:
1.使用self.navigationItem.leftBarButtonItem自定義自己的返回按鈕
2.通過[[UINavigationBar appearance] setBackIndicatorImage:[UIImage imageNamed:@"xx"]],[[UINavigationBar appearance] setBackIndicatorTransitionMaskImage::[UIImage imageNamed:@"xx"]];來修改返回圖片
上面的兩種方式我就不具體說明他們會存在什么樣的問題了,我相信試過的人都應該會遇到過。
使用系統默認的返回按鈕
原本我以為使用下面的方法應該可以達到效果
+ (void)initialize{
UINavigationBar * appearance = [UINavigationBar appearance];
[appearance setBackIndicatorImage:[UIImage imageNamed:@"icon_common_back"]];
[appearance setBackIndicatorTransitionMaskImage:[UIImage imageNamed:@"icon_common_back"]];
}
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
if (![viewController.navigationItem.backBarButtonItem.title isEqualToString:@""]) {
viewController.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc]
initWithTitle:@""
style:UIBarButtonItemStylePlain
target:nil
action:nil];
}
}```
但是出現的結果是這樣的

既然寬度不對,那么我就獲取對應的圖層重新設置它的寬度,但是試了之后雖然這個視圖可以達到效果,但是titleView 或者title的位置并沒有因為返回按鈕實際占位的減少而顯示正常(大家可以試試) ,可能有些人會說那么就獲取titleView這些圖層修改之,但是其實很復雜,我放棄了這種方法。
隨后我試了一下去掉圖片
-
(void)initialize{
UINavigationBar * appearance = [UINavigationBar appearance];
[appearance setBackIndicatorImage:[[UIImage alloc] init]];
[appearance setBackIndicatorTransitionMaskImage:[[UIImage alloc] init]];
}```
效果是這樣的并且同樣將文字去掉,發現這個時候
2784aa3a-ddcf-4ad5-8e8c-8f74559bdc74.png
那么問題就簡單了,那我就自己放一個imageView上去就把所有問題解決了。最后的效果是這樣的,具體的實現邏輯看我這里的代碼吧
https://github.com/BulletWu/BTNavigationBar
返回事件攔截
網上很多返回按鈕事件攔截的帖子,但是大多都是僅限于點擊返回按鈕的事件攔截,代碼也大同小異和下面一樣,當然我也是這樣去實現的。
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item{
if([self.viewControllers count] < [navigationBar.items count]) {
return YES;
}
UIViewController *topVC = self.topViewController;
BOOL shouldPop = YES;
if([topVC respondsToSelector:@selector(navigationShouldPopOnBackButton)]) {
shouldPop = [topVC navigationShouldPopOnBackButton];
}
if (topVC.hidesBackButton) {
shouldPop = NO;
}
if (shouldPop) {
id<UIViewControllerTransitionCoordinator> coor = topVC.transitionCoordinator;
if (coor && coor.initiallyInteractive) {
return YES;
}
NSInteger itemCount = self.navigationBar.items.count;
NSInteger n = self.viewControllers.count >= itemCount ? 2 : 1;
UIViewController *popToVC = self.viewControllers[self.viewControllers.count - n];
[self popToViewController:popToVC animated:YES];
return YES;
}else{
for(UIView *subview in [navigationBar subviews]) {
if(0. < subview.alpha && subview.alpha < 1.) {
[UIView animateWithDuration:.25 animations:^{
subview.alpha = 1.;
}];
}
}
return NO;
}
}
但是該方法無法攔截側滑返回的手勢,但是既然有點擊返回按鈕的手勢攔截,當然必須應該有側滑返回的手勢,但是系統側滑手勢設置的delegate對象為_UINavigationInteractiveTransition,是一個私有API,我們沒辦法覆寫它。但是手勢對象是interactivePopGestureRecognizer,是一個UIGestureRecognizer,而且可以想象系統一定是執行了這樣一句代碼self. interactivePopGestureRecognizer.delegate = _UINavigationInteractiveTransition實例對象,
那么我們的突破口就是重寫UIGestureRecognizer的setDelegate方法,但是我不想在UINavigationController中將interactivePopGestureRecognizer.delegate設置為self,因為我不能確定我能很好的控制這個delegate事件,事實證明,如果自己去設置也的確會出現這樣那樣的問題,那么我就使用消息轉發機制來實現,不多說了,還是看代碼吧
- (void)bt_navSetDelegate:(id<UIGestureRecognizerDelegate>)delegate{
if ([NSStringFromClass([delegate class]) isEqualToString:@"_UINavigationInteractiveTransition"]) {
if (delegate == nil) {
delegate = self;
}
[self bt_navSetDelegate:self];
self.gestureRecognizerDelegate = delegate != self ? delegate :nil;
}else{
[self bt_navSetDelegate:delegate];
}
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
UIViewController *topViewController = [[self class] getCurrentVC];
BOOL shouldPop = YES;
if([topViewController respondsToSelector:@selector(navigationShouldPopOnPopGestureRecognizer)]) {
shouldPop = [topViewController navigationShouldPopOnPopGestureRecognizer];
}
return shouldPop && [self.gestureRecognizerDelegate gestureRecognizerShouldBegin:gestureRecognizer];
}```
對于返回事件的一些事件我目前只做了
@protocol BTBackHandlerProtocol <NSObject>
@optional
- (BOOL)navigationShouldPopOnBackButton; //是否允許觸發返回按鈕
- (BOOL)navigationShouldPopOnPopGestureRecognizer; //側滑返回
- (void)navigationDidPop;//pop成功 ,因為側滑返回可能取消
- (void)navigationPopCancel;//側滑返回取消
@end
我想有這些應該足夠了
**導航欄透明交互**
主要思想就是不隱藏導航欄,只修改透明度,具體看代碼吧
https://github.com/BulletWu/BTNavigationBar
**交流進步**
因為這個我自己也是剛寫的,希望大家來幫助我發現問題,然后解決問題,做一個完美的導航欄