06.项目实战 百思不得姐 精华子控制器view懒加载,监听状态
@(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];
}