截取iOS系統返回事件
阿新 • • 發佈:2017-06-28
nbsp 開始 第一個 () 有時 miss lan 包含 操作
當我們使用了系統的導航欄時,默認點擊返回按鈕是 pop 回上一個界面。但是在有時候,我們需要在點擊導航欄的返回按鈕時不一定要 pop 回上一界面,比如一個視頻播放界面,進入橫屏後,默認點擊返回按鈕仍然是 pop 返回上一個界面,但是如果我們想要在橫屏點擊返回按鈕的時候是返回豎屏模式,而不是 pop 到上一界面,這該怎麽實現呢? 註意:我們要的不是獲取點擊返回按鈕的時機,而是想要攔截點擊返回按鈕的 pop 操作,使我們可以進行選擇性的 pop,而不是必然的 pop。 下面一步步來解決這個問題。 一、自定義返回按鈕 第一種,自定義導航欄的返回按鈕,雖然這看起來是一種方式,但是也不能從根本上解決;比如整個應用的返回鍵都是統一的,這時候再重寫了某個界面的返回按鈕感覺就不統一了。而且每有一個界面有這個需求都需要重新自定義一個返回按鈕,顯得不優雅。 自定義返回按鈕的方法很簡單,如下: // 自定義返回按鈕 - (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) { // 進入豎屏 [self enterPortrait]; return; } // pop [self.navigationController popViewControllerAnimated:YES]; } 二、為 UINavigationController 添加 category 此方法來自 github:UIViewController-BackButtonHandler 由於系統的 UINavigationController 使用了一個 UINavigationBar 來管理 Controller 的 pop 和 push 等操作,所以仔細查看 UINavigationBar 的 API,會發現一個 UINavigationBarDelegate,它包含了四個方法: - (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; 我們會驚喜的第一個不就是我們想要的效果嗎?因為該方法返回 YES 則 pop,若返回NO,則不POP。 因此我們可以為 UINavigatonController 創建一個 Category,來定制navigationBar: shouldPopItem: 的邏輯。這裏需要註意的是,我們不需要去設置 delegate,因為 UINavigatonController 自帶的 UINavigationBar 的 delegate 就是導航欄本身。這樣還有個問題就是,那在實際的 Controller 裏面怎麽控制呢?因此同樣需要對 UIViewController 添加一個 Protocol,這樣在 Controller 中使用該 Protocol 提供的方法即可進行控制了,代碼如下: // UIViewController+BackButtonHandler.h @protocol BackButtonHandlerProtocol <NSObject> @optional // 重寫下面的方法以攔截導航欄返回按鈕點擊事件,返回 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 後,復原返回按鈕的狀態 for(UIView *subview in [navigationBar subviews]) { if(0. < subview.alpha && subview.alpha < 1.) { [UIView animateWithDuration:.25 animations:^{ subview.alpha = 1.; }]; } } } return NO; } 到這兒,幾乎就完美的解決一開始的問題了,使用方法也非常簡單 (1). 在一個 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]; } }
截取iOS系統返回事件