當(dāng)我們使用了系統(tǒng)的導(dǎo)航欄時(shí)馁菜,默認(rèn)點(diǎn)擊返回按鈕是 pop 回上一個(gè)界面。但是在有時(shí)候旅急,我們需要在點(diǎn)擊導(dǎo)航欄的返回按鈕時(shí)不一定要 pop 回上一界面些椒,比如一個(gè)視頻播放界面,進(jìn)入橫屏后,默認(rèn)點(diǎn)擊返回按鈕仍然是 pop 返回上一個(gè)界面,但是如果我們想要在橫屏點(diǎn)擊返回按鈕的時(shí)候是返回豎屏模式,而不是 pop 到上一界面瓮增,這該怎么實(shí)現(xiàn)呢?
注意:我們要的不是獲取點(diǎn)擊返回按鈕的時(shí)機(jī)哩俭,而是想要攔截點(diǎn)擊返回按鈕的 pop 操作绷跑,使我們可以進(jìn)行選擇性的 pop,而不是必然的 pop凡资。
下面一步步來解決這個(gè)問題砸捏。
一、自定義返回按鈕
第一種隙赁,自定義導(dǎo)航欄的返回按鈕垦藏,雖然這看起來是一種方式,但是也不能從根本上解決伞访;比如整個(gè)應(yīng)用的返回鍵都是統(tǒng)一的掂骏,這時(shí)候再重寫了某個(gè)界面的返回按鈕感覺就不統(tǒng)一了。而且每有一個(gè)界面有這個(gè)需求都需要重新自定義一個(gè)返回按鈕厚掷,顯得不優(yōu)雅弟灼。
自定義返回按鈕的方法很簡單,如下:
// 自定義返回按鈕
- (void)customBackButton{
UIButton *backBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[backBtn setTitle:@"返回" forState:UIControlStateNormal];
[backBtn addTarget:self action:@selector(backBtnClicked:) forControlEvents:UIControlEventTouchUpInside];
backBtn.frame = CGRectMake(0, 0, 60, 40);
[backBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
UIBarButtonItem *item = [[UIBarButtonItem alloc]initWithCustomView:backBtn];
self.navigationItem.leftBarButtonItem = item;
}
// 返回按鈕按下
- (void)backBtnClicked:(UIButton *)sender{
if (_isLandscape) {
// 進(jìn)入豎屏
[self enterPortrait];
return;
}
// pop
[self.navigationController popViewControllerAnimated:YES];
}
二蝗肪、為 UINavigationController 添加 category
此方法來自 github:UIViewController-BackButtonHandler
由于系統(tǒng)的 UINavigationController
使用了一個(gè) UINavigationBar
來管理 Controller 的 pop 和 push 等操作袜爪,所以仔細(xì)查看 UINavigationBar
的 API蠕趁,會(huì)發(fā)現(xiàn)一個(gè) UINavigationBarDelegate
薛闪,它包含了四個(gè)方法:
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPushItem:(UINavigationItem *)item; // called to push. return NO not to.
- (void)navigationBar:(UINavigationBar *)navigationBar didPushItem:(UINavigationItem *)item; // called at end of animation of push or immediately if not animated
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item; // same as push methods
- (void)navigationBar:(UINavigationBar *)navigationBar didPopItem:(UINavigationItem *)item;
我們會(huì)驚喜的第一個(gè)不就是我們想要的效果嗎?因?yàn)樵摲椒ǚ祷?YES 則 pop俺陋,若返回NO豁延,則不POP昙篙。
因此我們可以為 UINavigatonController 創(chuàng)建一個(gè) Category,來定制navigationBar: shouldPopItem:
的邏輯诱咏。這里需要注意的是苔可,我們不需要去設(shè)置 delegate,因?yàn)?UINavigatonController 自帶的 UINavigationBar 的 delegate 就是導(dǎo)航欄本身袋狞。這樣還有個(gè)問題就是焚辅,那在實(shí)際的 Controller 里面怎么控制呢?因此同樣需要對 UIViewController 添加一個(gè) Protocol苟鸯,這樣在 Controller 中使用該 Protocol 提供的方法即可進(jìn)行控制了同蜻,代碼如下:
// UIViewController+BackButtonHandler.h
@protocol BackButtonHandlerProtocol <NSObject>
@optional
// 重寫下面的方法以攔截導(dǎo)航欄返回按鈕點(diǎn)擊事件,返回 YES 則 pop早处,NO 則不 pop
-(BOOL)navigationShouldPopOnBackButton;
@end
@interface UIViewController (BackButtonHandler) <BackButtonHandlerProtocol>
@end
// UIViewController+BackButtonHandler.m
@implementation UIViewController (BackButtonHandler)
@end
@implementation UINavigationController (ShouldPopOnBackButton)
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item {
if([self.viewControllers count] < [navigationBar.items count]) {
return YES;
}
BOOL shouldPop = YES;
UIViewController* vc = [self topViewController];
if([vc respondsToSelector:@selector(navigationShouldPopOnBackButton)]) {
shouldPop = [vc navigationShouldPopOnBackButton];
}
if(shouldPop) {
dispatch_async(dispatch_get_main_queue(), ^{
[self popViewControllerAnimated:YES];
});
} else {
// 取消 pop 后湾蔓,復(fù)原返回按鈕的狀態(tài)
for(UIView *subview in [navigationBar subviews]) {
if(0. < subview.alpha && subview.alpha < 1.) {
[UIView animateWithDuration:.25 animations:^{
subview.alpha = 1.;
}];
}
}
}
return NO;
}
到這兒,幾乎就完美的解決一開始的問題了砌梆,使用方法也非常簡單
(1). 在一個(gè) Controller 中:
#import "UIViewController+BackButtonHandler.h"
(2). 重寫 navigationShouldPopOnBackButton 方法
- (BOOL)navigationShouldPopOnBackButton{
[[[UIAlertView alloc] initWithTitle:@"提示" message:@"確定返回上一界面?"
delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"確定", nil] show];
return NO;
}
// UIAlertViewDelegate
- (void)alertView:(UIAlertView *)alertView willDismissWithButtonIndex:(NSInteger)buttonIndex{
if (buttonIndex==1) {
[self.navigationController popViewControllerAnimated:YES];
}
}