@(iOS 項目實戰)[項目實戰]
- 作者: Liwx
- 郵箱: 1032282633@qq.com
目錄
- 06.項目實戰 百思不得姐 精華子控制器view懶加載,監聽狀態欄點擊,tabBarButton重復點擊監聽
- 【相關知識點補充】
- UIScrollView動畫滾動方式
- UIScrollView監聽停止滾動
- 坐標系轉換
- 判斷是否重疊
- 導航條按鈕顯示異常bug
- 狀態欄點擊事件
- UIWindow相關知識點
- 監聽按鈕事件
- 1.精華子控制器view懶加載
- 子控制器view懶加載實現
- 2.監聽頂部狀態欄區域的點擊
- 監聽頂部狀態欄的點擊事件的實現
- 3.狀態欄點擊控制tableView滾動
- 查找所有的scrollView
- 4.監聽tabBarButton的重復點擊
- 監聽tabBarButton重復點擊方式一(使用tabBarButton addTarget方式,本項目使用此方式)
- 監聽tabBarButton重復點擊方式二(使用UITabBarController的代理方式)
- 子控制器監聽tabBarButton重復點擊通知
【相關知識點補充】
UIScrollView動畫滾動方式
- 1.使用
setContentOffset:animated:
方法實現動畫滾動. - 2.
scrollRectToVisiable:animated:滾動一塊特定的區域到scrollView顯示
.如果該區域已經在scrollView中可見,調用此方法沒反應.
以上兩個方法animated為YES才能實現動畫滾動.
UIScrollView監聽停止滾動
-
監聽UIScrollView停止滾動的四種方法
- 方式一: 當用戶
停止拖拽scrollView的時候調用
(手松開) - 方式二: 當
scrollView停止滾動的時候調用
- 方式三: 當
scrollView停止滾動的時候調用.前提:當使用setContentOffset:animated:或者scrollRectToVisible:animated:方法讓scrollView產生了滾動動畫
- 方式四: 使用UIView
animateWithDuration:animations:completion:方法
,animations block內部修改了scrollView的contentOffset的值,在completion 的block監聽scrollView滾動完成.
- 監聽UIScrollView停止滾動的幾種實現參考代碼
#pragma mark - <UIScrollViewDelegate> // ---------------------------------------------------------------------------- // 方式一 /** * 當用戶停止拖拽scrollView的時候調用(手松開) * 如果參數decelerate為YES,手松開后會繼續滾動,滾動完畢后會調用scrollViewDidEndDecelerating:代理方法 * 如果參數decelerate為NO,手松開后不再滾動,馬上靜止,也不會調用scrollViewDidEndDecelerating:代理方法 */ - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { if (decelerate) { NSLog(@"用戶停止拖拽scrollView,scrollView會繼續滾動"); } else { NSLog(@"用戶停止拖拽scrollView,scrollView不再滾動"); } } // ---------------------------------------------------------------------------- // 方式二 /** * 當scrollView停止滾動的時候調用 * 前提:人為手動讓scrollView產生滾動 */ - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { NSLog(@"用戶停止拖拽scrollView后滾動完畢"); } // ---------------------------------------------------------------------------- // 方式三 /** * 當scrollView停止滾動的時候調用 * 前提:當使用setContentOffset:animated:或者scrollRectToVisible:animated:方法讓scrollView產生了滾動動畫 */ - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView { NSLog(@"通過setContentOffset:animated:或者scrollRectToVisible:animated:方法讓scrollView產生滾動動畫,然后停止滾動"); } // ---------------------------------------------------------------------------- // 方式四 [UIView animateWithDuration:1.0 animations:^{ self.scrollView.contentOffset = CGPointMake(150, 150); [self.scrollView setContentOffset:CGPointMake(150, 150)]; } completion:^(BOOL finished) { NSLog(@"減速完畢----"); }];
- 方式一: 當用戶
坐標系轉換
- 計算控件A在window中的x,y,width,height.
- convertRect:toView:方法(兩個方法是可逆的)
- convertRect:fromView:方法
-
注意
: toView和fromView如果為nil,表示window. - 如果方法的
調用者是A本
身,則傳入的convertRect為A.bounds
.
CGRect rect = [A convertRect:A.bounds toView:nil]; - 如果方法的
調用者是A的父控件
,則傳入的convertRect為A.frame
.
// 描述控件A在window中的x\y\width\height
CGRect rect = [A.superview convertRect:A.frame toView:window];
CGRect rect = [A.superview convertRect:A.frame toView:nil];
CGRect rect = [A convertRect:A.bounds toView:window];
CGRect rect = [A convertRect:A.bounds toView:nil];
CGRect rect = [window convertRect:A.frame fromView:A.superview];
CGRect rect = [window convertRect:A.bounds fromView:A];
-
坐標系轉換圖解
坐標系轉換圖解.png
判斷是否重疊
-
使用
bool CGRectIntersectsRect(CGRect rect1, CGRect rect2)
函數判斷rect1與rect2-
注意
: 該函數的參數rect1和rect2必須處于同一坐標系
. 如果在不同坐標系,必須先進行坐標系轉換,再用此方法判斷兩個控件是否重疊.
- 判斷是否重疊參考代碼
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { CGRect rect1 = [self.blueView convertRect:self.blueView.bounds toView:nil]; CGRect rect2 = [self.redView convertRect:self.redView.bounds toView:nil]; BOOL result = CGRectIntersectsRect(rect1, rect2); NSLog(@"%zd", result); }
-
封裝UIView分類,實現判斷是否重疊的方法
- (BOOL)wx_intersectWithView:(UIView *)view
{
// 如果傳入的參數是nil,則表示為[UIApplication sharedApplication].keyWindow
if (view == nil) view = [UIApplication sharedApplication].keyWindow;
// 都統一轉換成window坐標系,并判斷是否重疊,返回判斷結果
CGRect rect1 = [self convertRect:self.bounds toView:nil];
CGRect rect2 = [view convertRect:view.bounds toView:nil];
return CGRectIntersectsRect(rect1, rect2);
}
導航條按鈕顯示異常bug
- 異常bug現象: 導航條的按鈕會出現位置有誤.只要push到其他控制器再返回時,導航條按鈕就顯示正常..
- 導航欄按鈕顯示出現問題,push/pop回來就好了的bug.原因是
重寫viewWillAppear:時,沒調用super 的viewWillAppear:方法或者調用錯方法導致
.
- 導航欄按鈕顯示出現問題,push/pop回來就好了的bug.原因是
// 如果super的方法名寫錯,會出現界面顯示的一些小問題
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
狀態欄點擊事件
-
系統默認點擊狀態欄的時候,scrollView滾動到頂部
- 前提是
UIScrollView的scrollsToTop屬性為YES
時,并且屏幕上只有一個scrollView
時才能用. - UIScrollView的
scrollsToTop屬性默認為YES
.
- 前提是
要想window里面的內容跟隨屏幕旋轉,那么必須設置window的rootViewController
-
狀態欄的樣式和顯示隱藏由
最頂層window的控制器決定
-
- (BOOL)prefersStatusBarHidden
: 顯示和隱藏 -
- (UIStatusBarStyle)preferredStatusBarStyle
:白色和黑色
-
旋轉
狀態欄消失
的原因是最上面window控制器沒實現狀態欄preferStatusBarHidden
方法.
UIWindow相關知識點
window顯示只要設置
hidden = NO就可以顯示
了,不需要像UIView要添加到父控件view上
.-
window級別越高,越顯示在頂層.
- UIWindowLevelAlert -> UIWindowLevelStatusBar ->UIWindowLevelNormal
- 如果級別一樣,越后面顯示在越頂層.
- 為什么一定要
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
方法中給window添加根控制器?- 只要有設置window的根控制器,window
里面的內容
才會跟隨旋轉.window本身是不會旋轉的.
- 只要有設置window的根控制器,window
監聽按鈕事件
- 監聽短時間按鈕多次點擊(雙擊)事件
UIControlEventTouchDownRepeat
1.精華子控制器view懶加載
子控制器view懶加載實現
-
1.實現添加index位置對應的子控制器view到scrollView參考代碼()
1.根據索引獲取子控制器(方法一)
-
2.判斷子控制器的view是否已經加載過(三種方法),如果已經加載過,退出
- 方法一:
childVc.isViewLoaded 判斷是否已經加載過
- 方法二:
childVc.view.superview 判斷是否有父控件
- 方法三:
childVc.view.window 判斷window是否有值
- 方法四: self.scrollView.subviews containsObject:childVc.view 判斷控制器的view是否在scrollView的子控件數組中.
- 方法一:
-
3.設置要添加的子控制器view的frame,并添加到scrollView
- 3.1 設置x值
- 3.2 需將y設置為0,因為childVc.view是UITableView,UITableView默認的y值是20
- 3.3 默認UITableView的高度是屏幕的高度減去它本身的y值(20),所以重新設置高度為整個scrollView的高度
- 3.4 添加子控制器的view到scrollView
// ---------------------------------------------------------------------------- // 添加index位置對應的子控制器view到scrollView - (void)addChildVcViewIntoScrollView:(NSInteger)index { // 1.根據索引獲取子控制器 UIViewController *childVc = self.childViewControllers[index]; // TODO: 2.判斷子控制器的view是否已經加載過,如果已經加載過,退出 // 方法一: childVc.isViewLoaded 方法二: childVc.view.superview 方法三: childVc.view.window if (childVc.isViewLoaded) { return; } // 3.設置要添加的子控制器view的frame,并添加到scrollView // 3.1 設置x值 childVc.view.wx_x = index * self.scrollView.wx_width; // 3.2 需將y設置為0,因為childVc.view是UITableView,UITableView默認的y值是20 childVc.view.wx_y = 0; // 3.3 默認UITableView的高度是屏幕的高度減去它本身的y值(20),所以重新設置高度為整個scrollView的高度 childVc.view.wx_height = self.scrollView.wx_height; // 3.4 添加子控制器的view到scrollView [self.scrollView addSubview:childVc.view]; }
- 使用偏移量計算索引值(方法二)
使用偏移量計算索引,實現添加子控制器view到scrollView,該方法只能通過偏移量來控制要顯示那個view
,靈活性不夠 (不推薦,因為其依賴偏移量
)- 經過以下分析
scrollView的x,y偏移量等于bounds的x,y值
.可推斷出childVc.view.frame剛好為scrollView.bounds
;
- 經過以下分析
// ---------------------------------------------------------------------------- // 使用偏移量計算索引,實現添加子控制器view到scrollView,該方法只能通過偏移量來控制要顯示那個view,靈活性不夠 (不推薦,因為其依賴偏移量) - (void)addChildVcViewIntoScrollView { NSInteger index = self.scrollView.contentOffset.x / self.scrollView.xmg_width; UIViewController *childVc = self.childViewControllers[index]; childVc.view.frame = self.scrollView.bounds; [self.scrollView addSubview:childVc.view]; // childVc.view.xmg_x = self.scrollView.bounds.origin.x; // childVc.view.xmg_y = self.scrollView.bounds.origin.y; // childVc.view.xmg_width = self.scrollView.bounds.size.width; // childVc.view.xmg_height = self.scrollView.bounds.size.height; // childVc.view.xmg_x = self.scrollView.contentOffset.x; // childVc.view.xmg_y = self.scrollView.contentOffset.y; // childVc.view.xmg_width = self.scrollView.xmg_width; // childVc.view.xmg_height = self.scrollView.xmg_height; // childVc.view.xmg_x = index * self.scrollView.xmg_width; // childVc.view.xmg_y = 0; // childVc.view.xmg_width = self.scrollView.xmg_width; // childVc.view.xmg_height = self.scrollView.xmg_height; }
2.在初始化添加子控制器的方法中調用添加子控制器的view的方法
設置默認顯示第0個子控制器的view
.
// ----------------------------------------------------------------------------
// 添加子控制器
- (void)setupAllChildViewController
{
// 1.添加5個子控制器
[self addChildViewController:[[WXAllViewController alloc] init]];
[self addChildViewController:[[WXVideoViewController alloc] init]];
[self addChildViewController:[[WXVoiceViewController alloc] init]];
[self addChildViewController:[[WXPictureViewController alloc] init]];
[self addChildViewController:[[WXWordViewController alloc] init]];
// 2.獲取子控制器數量
NSInteger count = self.childViewControllers.count;
// 設置默認顯示第0個子控制器的view
[self addChildVcViewIntoScrollView:0];
// 3.設置scrollView的滾動范圍
self.scrollView.contentSize = CGSizeMake(count * self.scrollView.wx_width, 0);
}
- 3.使用按鈕的tag值作為索引,在下劃線動畫執行完成修改scrollView的偏移量,顯示對應子控制器的view.
- 如果對應子控制器的view為添加到scrollView,則添加view到scrollView,并更新偏移量顯示對應view.
#pragma =======================================================================
#pragma mark - titleButton按鈕點擊
// ----------------------------------------------------------------------------
// 監聽按鈕點擊
- (void)titleButtonClick:(WXTitleButton *)button
{
// 切換中狀態
self.selectedButton.selected = NO;
button.selected = YES;
self.selectedButton = button;
// 1.獲取索引,按鈕的tag值
NSInteger index = button.tag;
// 2.執行下劃線動畫,動畫執行完成修改scrollView的偏移量,顯示對應子控制器的view
[UIView animateWithDuration:0.25 animations:^{
// TODO: 設置下劃線的寬度和中心點
self.underLineView.wx_width = button.titleLabel.wx_width;
self.underLineView.wx_centerX = button.wx_centerX;
// 切換到對應的view
self.scrollView.contentOffset = CGPointMake(self.scrollView.wx_width * index, self.scrollView.contentOffset.y);
} completion:^(BOOL finished) {
// 更新偏移量
CGPoint offset = self.scrollView.contentOffset;
offset.x = index * self.scrollView.wx_width;
[self.scrollView setContentOffset:offset];
// 添加對應子控制器的view
[self addChildVcViewIntoScrollView:index];
}];
}
2.監聽頂部狀態欄區域的點擊
-
實現思路分析
- 創建一個級別為
UIWindowLevelAlert(最高)
的且占據全屏的WXTopWindow
,并設置其hidden = NO
,讓其顯示在最頂層.
- 在
WXTopWindow(繼承UIWindow)
中重寫
hitTest:withEvent:方法
實現響應高度( <= 20 )也就是
藍色區域(狀態欄區域)
能點擊,紅色區域( >20 )
不能點擊
-
創建一個WXTopViewController
(繼承UIViewController),并
WXTopViewController設置為WXTopWindow的根控制器
.
因為WXTopWindow已經設置僅頂部狀態欄區域能點擊
,所以
重寫WXTopViewController的view的touchesBegan方法就可以 僅監聽狀態欄區域的點擊
事件.
- 創建一個級別為
-
監聽狀態欄點擊實現解析圖
監聽狀態欄點擊實現解析圖.png
監聽頂部狀態欄的點擊事件的實現
-
監聽狀態欄點擊事件實現
- 1.自定義WXTopWindow(繼承UIWindow)
- 2.提供一個
參數為block的的類方法
,供外部調用.block會在狀態欄區域被點擊的時候調用
+ (void)showWithStatusBarClickBlock:(void (^)())block;
- 3.在
WXTopWindow(繼承UIWindow)
中重寫hitTest:withEvent:方法
實現只響應狀態欄區域的點擊. - 4.showWithStatusBarClickBlock:類方法的實現
- 1.判斷如果該window已經創建,無需再創建,因為window整個應用程序只需要一個,無需重復創建
- 2.添加window到狀態欄區域,window默認填充整個屏幕,所以無需設置frame
- 3.設置window的優先級為最高,比狀態欄的優先級高
UIWindowLevelAlert(高) UIWindowLevelStatusBar(中) UIWindowLevelNormal(低,默認) - 4.設置window的背景色為透明色
- 注意: 需
先顯示window再設置根控制器
. - 5.創建WXTopViewController并設置背景色為clearColor
- 6.設置
旋轉時只拉伸寬度
,否則會出現view的狀態欄區域旋轉消失或變大問題
.
控制器的view默認是長度寬度都自動拉伸,此處只需拉伸寬度,不需要拉伸高度. - 7.將block傳遞給控制器管理,當控制器的view的狀態欄區域被點擊,調用block
- 8.設置WXTopWindow的根控制器為WXTopViewController.只有
設置window的根控制器,window里面的內容
才會跟隨旋轉.window本身不會旋轉
.
- 5.在WXTopViewController控制器中實現touchesBegan:方法監聽頂部狀態欄區域的點擊.
- touchesBegan:方法監聽到頂部狀態欄區域的點擊,調用WXTopWindow傳遞過來的block.
-
注意
: WXTopViewController必須明確指定狀態欄顯示,否則會出現旋轉后狀態欄消失.
- 使用方法: 調用showWithStatusBarClickBlock:即可實現監聽狀態欄區域的點擊.
[WXTopWindow showWithStatusBarClickBlock:^{ NSLog(@"點擊了頂部狀態欄區域"); }];
3.狀態欄點擊控制tableView滾動
查找所有的scrollView
- 1.首先實現UIView的分類方法: 判斷方法調用者和view(本項目功能指keyWindow)是否重疊
// ----------------------------------------------------------------------------
// 判斷方法調用者和view是否重疊
- (BOOL)wx_intersectWithView:(UIView *)view
{
// 如果傳入的參數是nil,則表示為[UIApplication sharedApplication].keyWindow
if (view == nil) {
view = [UIApplication sharedApplication].keyWindow;
}
// 都統一轉換成window坐標系,并判斷是否重疊,返回判斷結果
CGRect rect1 = [self convertRect:self.bounds toView:nil];
CGRect rect2 = [view convertRect:view.bounds toView:nil];
return CGRectIntersectsRect(rect1, rect2);
}
-
2.查找出window里面的所有scrollView
- 1.判斷是否在keyWindow的范圍內(不跟window重疊),如果不在,直接退出
- 2.遍歷view的所有子控件和子控件的子控件,此處for循環會退出,所以遞歸調用會退出
- 3.判斷如果scrollView,直接返回
- 4.滾動scrollView到最頂部
- 方法一: 獲取scrollView,
將scrollView的偏移量y值設置為負的內邊距頂部值: -scrllView.contentInset.top
- 方法一: 獲取scrollView,
UIScrollView *scrllView = (UIScrollView *)view; CGPoint offset = scrllView.contentOffset; offset.y = -scrllView.contentInset.top; [scrllView setContentOffset:offset animated:YES];
- 方法二: 讓`scrollView移動到其內容的最頂部`. ```objectivec [scrollView scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:YES]; ```
- 使用: 在
didFinishLaunchingWithOptions:
方法中調用UIView的分類對象方法,searchAllScrollViewsInView:方法
,傳入application.keyWindow
參數,判斷scrollView是否在keyWindow中,如果在keyWindow中,則滾動scrollView到頂部
.
- 實現當前顯示的scrollView/tableView
點擊頂部狀態欄區域滾動到scrollView/tableView最頂部
參考代碼
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // 1.創建window self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; // 2.設置window的根控制器 self.window.rootViewController = [[WXAdViewController alloc] init]; // init -> initWithNibName -> 1.判斷有沒有指定NibName 2.判斷有沒有跟控制器同名的xib,就會去加載 3.判斷下有沒有不帶controller的xib 4.創建一個clearColor透明的View // 3.讓window成為主窗口,并顯示 [self.window makeKeyAndVisible]; // 4.添加topWindow [WXTopWindow showWithStatusBarClickBlock:^{ [self searchAllScrollViewsInView:application.keyWindow]; }]; return YES; } // ---------------------------------------------------------------------------- // 查找出view里面的所有scrollView - (void)searchAllScrollViewsInView:(UIView *)view { // 1.判斷是否在keyWindow的范圍內(不跟window重疊),如果不在window范圍內,直接退出 if (![view wx_intersectWithView:nil]) { return; } // 2.遍歷view的所有子控件和子控件的子控件,此處for循環會退出,所以遞歸調用會退出 for (UIView *subview in view.subviews) { [self searchAllScrollViewsInView:subview]; } // 3.判斷如果scrollView,直接返回 if (![view isKindOfClass:[UIScrollView class]]) { return; } // 4.滾動scrollView到最頂部 UIScrollView *scrllView = (UIScrollView *)view; CGPoint offset = scrllView.contentOffset; offset.y = -scrllView.contentInset.top; [scrllView setContentOffset:offset animated:YES]; }
4.監聽tabBarButton的重復點擊
-
運行效果圖
監聽tabBarButton的重復點擊效果圖.gif -
實現思路:
- 1.思考使用UITabBarButton的addTarget方式監聽(可行,簡單)
- 2.使用UITabBar代理方式監聽(經驗證,不可行)
原因: 被一個UITabBarController管理的tabBar的代理是不能被改變的.如果一個UITabBar被UITabBarController管理,又重新設置UITabBar的代理就會報錯,運行時報錯:reason: 'Changing the delegate of a tab bar managed by a tab bar controller is not allowed.
)
如果UITabBar沒有被UITabBarController管理,是可以修改它的代理的.示例代碼
// UITabBar沒有被UITabBarController管理,是可以修改它的代理的 UITabBar *tabBar = [[UITabBar alloc] init]; tabBar.delegate = self; [self.view addSubview:tabBar];
- 3.使用UITabBarController的代理監聽(可行)
監聽tabBarButton重復點擊方式一(使用tabBarButton addTarget方式,本項目使用此方式)
-
1.在layoutSubviews方法中獲取所有tabBarButton,使用addTarget方式監聽tabBarButton的點擊
- UITabBarButton是私有類,不能使用,所以打印其superClass,父類為UIControl,所以可以用addTarget方法監聽點擊事件
- 監聽tabBarButton重復點擊的兩種方式
- 1.記錄上一次點擊的tabBarButton的
tag值
.監聽到tabBarButton重復點擊,發送通知
,通知外界tabBarButton重復點擊。
- 1.記錄上一次點擊的tabBarButton的
// ---------------------------------------------------------------------------- // 重新布局tabBar子控件 - (void)layoutSubviews { [super layoutSubviews]; // 1.定義frame屬性 NSInteger count = self.items.count; CGFloat itemX = 0; CGFloat itemY = 0; CGFloat itemW = self.wx_width / (count + 1); CGFloat itemH = self.wx_height; // 2.遍歷子控件(過濾UITabBarButton), UITabBarButton是私有屬性 NSInteger index = 0; for (UIView *view in self.subviews) { // 2.1 過濾UITabBarButton // 可以用兩張方式判斷 // 1.[view isKindOfClass:NSClassFromString(@"UITabBarButton")] // 2.[@"UITabBarButton" isEqualToString:NSStringFromClass([view class])] if ([view isKindOfClass:NSClassFromString(@"UITabBarButton")]) { // 2.2 計算x值,并設置frame itemX = index * itemW; view.frame = CGRectMake(itemX, itemY, itemW, itemH); // 2.3 監聽UITabBarButton的點擊,打印view的父類為UIControl // TODO: 使用AddTarget監聽tabBarButton的點擊 UIControl *tabBarButton = (UIControl *)view; tabBarButton.tag = index; [tabBarButton addTarget:self action:@selector(tabBarButtonClick:) forControlEvents:UIControlEventTouchUpInside]; index++; // 判斷如果是是第二個batBarButton,空一格 if (index == 2) { index++; } } } // 3.設置加號按鈕 self.plusButton.center = CGPointMake(self.wx_width * 0.5, self.wx_height * 0.5); } // ---------------------------------------------------------------------------- // 使用記錄上一次點擊的tabBarButton的tag方法監聽tabBarButton的點擊 - (void)tabBarButtonClick:(UIControl *)tabBarButton { // 使用tabBarButton的tag方法監聽 if (self.selectedTabBarButton.tag == tabBarButton.tag) { // 發送通知 [[NSNotificationCenter defaultCenter] postNotificationName:WXTabBarButtonDidRepeatClickNotification object:nil]; } // 記錄選中tabBarButton self.selectedTabBarButton = tabBarButton; }
- 2.記錄上一次點擊的
tabBarButton
.監聽到tabBarButton重復點擊,發送通知
,通知外界tabBarButton重復點擊。
注意: 此方式有個bug
,在程序剛啟動完成,默認選中第0個tabBarButton,如果再點擊第0個tabBarButton時,并沒有觸發重復點擊.原因是因為記錄上一次點擊的tabBarButton默認為nil
.所以使用該方法需在合適的地方
給上一次點擊的tabBarButton賦一個初始值
.以下參考代碼存在以上所提的bug,解決此bug必須給上一次點擊的tabBarButton賦一個初始值. ```objectivec // ---------------------------------------------------------------------------- // 使用記錄上一次點擊的tabBarButton方法監聽tabBarButton的點擊 - (void)tabBarButtonClick:(UIControl *)tabBarButton { // TODO: 需注意程序剛啟動時self.selectedTabBarButton == nil的情況 if (self.selectedTabBarButton == tabBarButton) { [[NSNotificationCenter defaultCenter] postNotificationName:WXTabBarButtonDidRepeatClickNotification object:nil]; } // 記錄選中tabBarButton self.selectedTabBarButton = tabBarButton; } ```
監聽tabBarButton重復點擊方式二(使用UITabBarController的代理方式)
- 1.在廣告界面即將跳轉到TabBarController的地方,
為TabBarController設置代理
,代理對象是·[UIApplication sharedApplication].delegate
,不能用廣告控制器做為代理對象,因為一旦根控制器切換到TabBarController時,廣告控制器就會被銷毀
.
// ----------------------------------------------------------------------------
// 監聽點擊跳過按鈕
- (IBAction)jump {
// 關閉定時器
[self.timer invalidate];
WXTabBarController *tabBarVc = [[WXTabBarController alloc] init];
tabBarVc.delegate = (id<UITabBarControllerDelegate>)[UIApplication sharedApplication].delegate;
[UIApplication sharedApplication].keyWindow.rootViewController = tabBarVc;
}
- 2.在APPDelegate.m文件中,讓AppDelegate遵守UITabBarControllerDelegate協議,并實現
tabBarController:didSelectViewController:
代理方法,監聽TabBarController選中了哪個控制器.
// ----------------------------------------------------------------------------
// 監聽tabBarController當前選中哪個控制器
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
// selectedVc用于存放TabBarController上一次選中的控制器
static UIViewController *selectedVc = nil;
// 設置初始化上一次選中的控制器為tabBarController的第0個子控制器.
if (selectedVc == nil) {
selectedVc = tabBarController.childViewControllers[WXDefaultVcIndex];
}
// 如果上一次選中的控制器和當前選中控制器一樣,表示重復點擊,發送通知
if (selectedVc == viewController) {
[[NSNotificationCenter defaultCenter] postNotificationName:WXTabBarButtonDidRepeatClickNotification object:nil];
}
// 更新上一次選中控制器
selectedVc = viewController;
}
子控制器監聽tabBarButton重復點擊通知
- 在對應子控制器的viewDidLoad方法中監聽tabBarButton重復點擊通知,執行響應操作
- (void)viewDidLoad {
[super viewDidLoad];
WXFunc();
self.view.backgroundColor = WXRandomColor;
self.tableView.contentInset = UIEdgeInsetsMake(WXNavMaxY + WXTitlesViewH, 0, WXTabBarH, 0);
// 1.監聽通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(tabBarButtonDidRepeatClick) name:WXTabBarButtonDidRepeatClickNotification object:nil];
}
#pragma =======================================================================
#pragma mark - 監聽tabBarButton重復點擊通知
- (void)tabBarButtonDidRepeatClick
{
NSLog(@"%@: 重復點擊,執行下拉刷新", [self class]);
}
- (void)dealloc
{
// 移除通知
[[NSNotificationCenter defaultCenter] removeObserver:self];
}