您好,登錄后才能下訂單哦!
小編給大家分享一下iOS導航欄控制的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
1.導航欄的顯示與隱藏
導航欄的顯示與隱藏,分兩種情況:
1.從不顯示導航欄的頁面push到顯示導航欄的頁面。
2.從顯示導航欄的頁面Push到不顯示導航欄的頁面。
注意:
1.如果導航欄不顯示時,系統的側滑返回功能無效。
2.雖然側滑返回功能無效,但是導航欄的 .interactivePopGestureRecognizer.delegate
還是存在的。
針對以上兩種情況分別處理,整個Push過程都假設是從A頁面跳轉到B頁面
1.1 從不顯示導航欄的頁面Push到顯示導航欄的頁面。
關于導航欄的顯示,是否順滑,是通過如下兩個方法來控制。
// 不顯示動畫,導航欄顯示就比較突兀[self.navigationController setNavigationBarHidden:YES];// 顯示動畫,在側滑時,導航欄顯示就比較順滑[self.navigationController setNavigationBarHidden:YES animated:YES];
所以,做法是:
A頁面:
- (void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; [self.navigationController setNavigationBarHidden:YES animated:YES];}
B頁面:
- (void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; [self.navigationController setNavigationBarHidden:NO animated:YES];}
1.2 從顯示導航欄的頁面跳轉到不顯示導航欄的頁面
這種情況的做法如下:
A頁面:
- (void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; [self.navigationController setNavigationBarHidden:NO animated:YES];}
B頁面:
// 在頁面將要出現時,記錄原始側滑手勢代理對象,并將手勢代理設置為當前頁面- (void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; self.interactivePopDelegate = self.navigationController.interactivePopGestureRecognizer.delegate; self.navigationController.interactivePopGestureRecognizer.delegate = self; [self.navigationController setNavigationBarHidden:YES animated:YES];}// 在頁面消失時,還原側滑手勢代理對象- (void)viewDidDisappear:(BOOL)animated{ [super viewDidDisappear:animated]; self.navigationController.interactivePopGestureRecognizer.delegate = self.interactivePopDelegate; self.interactivePopDelegate = nil;}// 實現手勢代理,為了防止影響其他手勢,可以判斷一下手勢類型- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{ if ([gestureRecognizer isKindOfClass:[UIScreenEdgePanGestureRecognizer class]]) { return YES; } ...... 其他手勢的處理 return NO;}
2.統一重寫導航欄返回按鈕
有時候,我們可能需要統一工程中的返回按鈕樣式,比如都是 箭頭+返回 或者都是 箭頭。
方案有兩種:
1.創建一個BaseViewController,然后統一設置navigationItem.leftBarButtonItem
。
2.重寫導航控制器的Push方法,在push之前,設置navigationItem.backBarButtonItem
。
注意:
如果重寫了導航欄的leftBarButtonItem,那么側滑返回功能也就失效了,需要側滑返回功能需要自己處理。
第一種方案比較簡單就不做贅述了,第二種方案是這樣的:
自定義導航控制器,然后重寫如下方法:
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{ UIBarButtonItem *backItem = [[UIBarButtonItem alloc] initWithTitle:@"返回" style:UIBarButtonItemStyleDone target:nil action:nil]; viewController.navigationItem.backBarButtonItem = backItem; [super pushViewController:viewController animated:animated];}
如果不需要返回這兩個字,只需要這樣寫就好。
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{ UIBarButtonItem *backItem = [[UIBarButtonItem alloc] initWithTitle:nil style:UIBarButtonItemStyleDone target:nil action:nil]; viewController.navigationItem.backBarButtonItem = backItem; [super pushViewController:viewController animated:animated];}
3.監聽返回按鈕的點擊事件
在有些場景,我們需要監聽返回按鈕的事件。比如,當頁面用戶輸入了一些內容后,用戶要點擊返回,想要回到上一個頁面時,提醒用戶是否要緩存已經輸入的內容。
如果我們重寫了導航欄的返回按鈕,那么處理這種情況就很Easy,不做贅述了。
但是,如果我們沒有重寫過系統的返回按鈕,想要處理這種情況就比較麻煩,但是也是可以處理的。
處理步驟如下:
1.首先創建一個UIViewController的類別,頭文件(.h)的內容如下:
@protocol BackItemProtocol <NSObject>- (BOOL)navigationShouldPopWhenBackButtonClick;@end@interface UIViewController (BackItem)<BackItemProtocol>@end@interface UINavigationController (BackItem)@end
包含一個協議、UIViewController的類別、UINavigationController的類別。
然后,實現文件(.m)如下:
#import "UIViewController+BackItem.h"@implementation UIViewController (BackItem)- (BOOL)navigationShouldPopWhenBackButtonClick{ return YES;}@end@implementation UINavigationController (BackItem)// 這個其實是導航欄的協議方法,在這里重寫了- (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(navigationShouldPopWhenBackButtonClick)]) { shouldPop = [vc navigationShouldPopWhenBackButtonClick]; } if (shouldPop) { dispatch_async(dispatch_get_main_queue(), ^{ [self popViewControllerAnimated:YES]; }); } else { for(UIView *subview in [navigationBar subviews]) { if(subview.alpha < 1) { [UIView animateWithDuration:.25 animations:^{ subview.alpha = 1; }]; } } } return NO;}@end
默認是,不需要處理返回按鈕的事件,直接使用系統的pop方法。
但是,如果我們需要在用戶點擊返回按鈕時,彈窗提示,那就需要導入這個類別。
然后,重寫一個方法:
- (BOOL)navigationShouldPopWhenBackButtonClick{ BOOL isFlag = 輸入框不為空等等條件 if (isFlag) { UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:nil message:@"是否保存修改" preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { // 這里延時執行是因為UIAlertController阻塞UI,可能會導致動畫的不流暢 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self.navigationController popViewControllerAnimated:YES]; }); }]; UIAlertAction *saveAction = [UIAlertAction actionWithTitle:@"保存" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { // 這里延時執行是因為UIAlertController阻塞UI,可能會導致動畫的不流暢 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self rightClick]; }); }]; [alertVC addAction:cancelAction]; [alertVC addAction:saveAction]; [self presentViewController:alertVC animated:YES completion:nil]; return NO; } return YES;}
4.導航控制器的頁面跳轉方式
安卓中的頁面跳轉有四種方式: standard、singleTop、singleTask、singleInstance。
例如singleTask,在做IM類App,跳轉到聊天室的場景,就非常有用,可以保證控制器棧中只有一個聊天室,避免返回時層級太深。
iOS端如果要仿這個效果的話,可以利用導航控制器的API:
- (void)setViewControllers:(NSArray<UIViewController *> *)viewControllers animated:(BOOL)animated
首先,為UINavigationController 創建一個類別。
比如:
UINavigationController+HLPushAndPop.hUINavigationController+HLPushAndPop.m
然后,新增幾個方法:
拿兩個方法來舉例
- (void)hl_pushSingleViewController:(UIViewController *)viewController animated:(BOOL)animated;- (void)hl_pushSingleViewController:(UIViewController *)viewController parentClass:(Class)parentClass animated:(BOOL)animated;
再然后,實現方法:
實現步驟:
創建新的數組復制導航控制器原來的堆棧中的控制器。在原始堆棧數組中判斷是否存在該類型的控制器,如果存在記錄其索引。在復制的數組中將索引及上方所有控制器移除。把將要push出來的控制器添加到復制的數組中。將新的控制器數組設置為導航控制器的棧數組,根據參數判斷是否要顯示動畫。
我這邊做了一些發散,因為一些類可能會有很多子類,那么想要保證父類以及子類的實例都只有一個,所以將方法做了改進。
- (void)hl_pushSingleViewController:(UIViewController *)viewController animated:(BOOL)animated{ [self hl_pushSingleViewController:viewController parentClass:viewController.class animated:animated];}- (void)hl_pushSingleViewController:(UIViewController *)viewController parentClass:(Class)parentClass animated:(BOOL)animated{ if (!viewController) { return; } // 如果要push的界面不是 parentClass以及其子類的實例,則按照方法1處理 if (![viewController isKindOfClass:parentClass]) { [self hl_pushSingleViewController:viewController animated:animated]; return; } // 判斷 導航控制器堆棧中是否有parentClass以及其子類的實例 NSArray *childViewControllers = self.childViewControllers; NSMutableArray *newChildVCs = [[NSMutableArray alloc] initWithArray:childViewControllers]; BOOL isExit = NO; NSInteger index = 0; for (int i = 0; i < childViewControllers.count; i++) { UIViewController *vc = childViewControllers[i]; if ([vc isKindOfClass:parentClass]) { isExit = YES; index = i; break; } } // 如果不存在,則直接push if (!isExit) { [self pushViewController:viewController animated:animated]; return; } // 如果存在,則將該實例及上面的所有界面全部彈出棧,然后將要push的界面放到棧頂。 for (NSInteger i = childViewControllers.count - 1; i >= index; i--) { [newChildVCs removeObjectAtIndex:i]; } [newChildVCs addObject:viewController]; viewController.hidesBottomBarWhenPushed = (newChildVCs.count > 1); [self setViewControllers:newChildVCs animated:animated];}
當然了,除了上面這些場景,還可以擴展出一些其他的場景,比如我們期望將要push出來的控制器再某個棧中控制器的后面或者前面,這樣當點擊返回或者側滑時,就直接回到了指定頁面了。
或者我們知道將要返回的頁面的類型,直接pop回指定頁面。
以上是“iOS導航欄控制的示例分析”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。