美文网首页
iOS 获取电池栏的点击事件

iOS 获取电池栏的点击事件

作者: 魔力双鱼 | 来源:发表于2018-12-25 10:11 被阅读0次

前言 设置scrollsToTop属性

系统默认是有 点击status bar ,scrollView自动滚动到顶部。如果一个页面有多个scrollView,就需要把不用滚动到顶部的scrollView.scrollToTop = No;

// When the user taps the status bar, the scroll view beneath the touch which is closest to the status bar will be scrolled to top, but only if its `scrollsToTop` property is YES, its delegate does not return NO from `-scrollViewShouldScrollToTop:`, and it is not already at the top.
当用户轻击状态栏时,触摸下方最接近状态栏的滚动视图将滚动到顶部,但只有它的“scrollsToTop”属性为YES时,它的delegate从“-scrollViewShouldScrollToTop:”不返回NO,并且它尚未位于顶部。

// On iPhone, we execute this gesture only if there's one on-screen scroll view with `scrollsToTop` == YES. If more than one is found, none will be scrolled.
在iPhone上,我们只有在屏幕上有一个“scrollsToTop”==YES.如果发现不止一个,则不会滚动。

@property(nonatomic) BOOL  scrollsToTop __TVOS_PROHIBITED;          // default is YES.
  • 但实际开发当中,我们的视图结构里大都包含了很多个scrollView,当视图中具备多个scrollView的时候:
  1. 我们要想实现唯一一个scrollView可以响应statusBar的手势,则只需将其他scrollView的scrollsToTop属性置为NO就可以了。
  2. 我们要想实现多个scrollView响应statusBar的手势,我们只能使用自定义的方式。

状态栏的层级级别

首先,statusBar是一个特殊的view,始终位于程序的topLevel,我们创建的普通view的level比statusBar低很多。因此响应事件优先被statusBar截获。
查阅官方文档,关于层级关系有如下定义:

const UIWindowLevel UIWindowLevelNormal;  
const UIWindowLevel UIWindowLevelAlert;  
const UIWindowLevel UIWindowLevelStatusBar;  
typedef CGFloat UIWindowLevel; 

从高到低依次是UIWindowLevelAlert > UIWindowLevelStatusBar >UIWindowLevelNormal
看到windowLevel我们立马想到UIWindow具有windowLevel这样一个属性。也就是说,如果我们想实现一个可以覆盖statusBar的view,我们需要将这个view继承自UIWindow,并且将层级Level设置的比UIWindowLevelStatusBar高才可以。

总结

系统的UIScrollView自带有点击顶部状态栏自动返回顶部的效果,其属性scrollsToTop的默认值是YES

  • 当视图中只有一个UIScrollView时,点击顶部状态栏自动返回顶部
  • 当视图中有多个UIScrollView时:
    如果只有一个UIScrollViewscrollsToTop属性值为YES,则该UIScrollView具有返回顶部的效果,其他UIScrollView不具有该效果。
    如果多个UIScrollViewscrollsToTop属性值为YES,则所有UIScrollView都不具有该效果。
    如果想同时让多个UIScrollView自动返回顶部,需要自定义一个可以覆盖statusBarUIWindow,而非UIView

0.需求 自定义一个TopWindow

实际开发中我们经常会有复杂的界面结构,其中会具有多个UIScrollView,我们要如何实现点击状态栏,让多个UIScrollView返回顶部呢?其实前面的思路已经很接近了,自定义一个可以覆盖statusBar的视图就可以了,当然为了更好的复用,本文中自定义了一个继承自NSObject的TopWindow。

实现代码

为了方便在复杂的界面逻辑中,很好的使用,我们自定义一个TopWindow继承自NSObject,由于在AppDelegate创建一个新的窗口必须给这个窗口设置一个根控制器,否则会报错,我们还要创建一个根控制器TopWindowRootVC。这样写的好处是可以在任意地方使用。

在TopWindow的.h中提供两个方法。

+(void)show;

+(void)hide;

.m的实现中,在initialize方法中对window进行初始化操作

windowTop = [[UIWindow alloc]initWithFrame:CGRectMake(0, 0, KBLScreenWidth, 20)];
windowTop.backgroundColor = [UIColor clearColor];
windowTop.windowLevel = UIWindowLevelStatusBar + 1.0f;
windowTop.rootViewController = [TopWindowRootVC new];

实现.h中提供的两个方法:

+(void)show{
    windowTop.hidden = NO;
}

+(void)hide{
    windowTop.hidden = YES;
}

TopWindowRootVC的touchBegan方法中完成操作,提供了一个searchView方法

UIWindow *window = [UIApplication sharedApplication].keyWindow;
[self searchView:window];

searchView方法中我们需要找到我们需要滚动的scrollView

-(void)searchNeedScrollView:(UIView *)window{
    [self dumpView:window atIndent:0];
    for (UIScrollView *scrollV in _arr) {
        if (/* 需要滚动的视图 */) {
            CGPoint offset = scrollV.contentOffset;
            offset.y = -scrollV.contentInset.top;
            [scrollV setContentOffset:offset animated:YES];
        }
    }
}

递归遍历window的所有子视图

- (void)dumpView:(UIView *)aView atIndent:(int)indent
{
    for (UIView *view in [aView subviews]){
        if ([view isKindOfClass:[UIScrollView class]]) {
            [_arr addObject:view];
        }
        [self dumpView:view atIndent:indent + 1];
    }
}

1.需求 通过坐标转换来获取电池栏的点击事件

在项目开发的时候遇到的问题,点击了电池栏让tableView滑动到顶部,UIScrollView有一个scrollsToTop属性,可以让滚动视图滑动到顶部,但是当视图中有很多scrollView包括第三方等中的视图,设置这个属性就会变得非常的麻烦,而且不一定能解决问题;
那么我们可以通过坐标转换来获取电池栏的点击事件;要先禁掉scrollsToTop属性。
类似于微信聊天界面中,点击statusbar,会回到tableview会回到顶点, 然后再次点击 会获取先前保存再本地的若干条数据。

思路:

1.获取用户在UIWindow上所点击的坐标

2.判断该坐标是否在电池栏​的矩形区域中

3.如果点击了电池栏的区域则发送一个通知

4.在需要的控制器里边添加观察者

5.执行点击事件

6.卸载监听

实现:

​ 在AppDelegate.h中定义一个全局量:

static NSString * const kStatusBarTappedNotification = @"statusBarTappedNotification";​

​在AppDelegate.m中判断用户点击左边以及是否点击在电池栏

#pragma mark - Status bar touch tracking

- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

    [super touchesBegan:touches withEvent:event];

    CGPoint location = [[[event allTouches] anyObject] locationInView:[self window]];

    CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame;

// 如果点击了电池栏则发送一个通知

    if (CGRectContainsPoint(statusBarFrame, location)) {

        [[NSNotificationCenter defaultCenter] postNotificationName:kStatusBarTappedNotification object:nil];

    }

}​

在需要电池栏点击事件的控制器中添加监听
Observe notification in the needed controller (e.g. in viewWillAppear):

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarTappedAction:) name:kStatusBarTappedNotification object:nil];​

实现监听的方法

- (void)statusBarTappedAction:(NSNotification*)notification {

    // 在这里写要处理电池栏的点击事件

}

最后卸载监听
Remove observer properly (e.g. in viewDidDisappear):

[[NSNotificationCenter defaultCenter] removeObserver:self name:kStatusBarTappedNotification object:nil];​

2.一个横屏的scrollView里面放置了多个tableView

最近做项目,由于一个横屏的scrollView里面放置了多个tableView,但点击状态栏仍要求tableView滚动到顶部,

思路是这样的:

1 追踪UIStatusBar的touch事件,然后响应该事件。不要直接向statusBar添加事件,因为并没有什么卵用!我也不知道为什么没有什么卵有!

2 找到UIApplication的keyWindow,遍历keyWindow的所有子控件。如果是scrollView,而且显示在当前keyWindow,那么将其contentOffset的y值设置为原始值(0)。这里会用到递归去进行遍历。

实现:

1.首先我们追踪UIStatusBar的触摸事件,需要在AppDelegate里面加入以下代码

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesBegan:touches withEvent:event];
    CGPoint location = [[[event allTouches] anyObject] locationInView:self.window];
    CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame;
    if (CGRectContainsPoint(statusBarFrame, location)) {
        [self statusBarTouchedAction];
    }
}

2.然后在statusBarTouchedAction方法中将显示在当前keyWindow里面的scrollView滚动到顶部

- (void)statusBarTouchedAction {
    [JMSUIScrollViewTool scrollViewScrollToTop];
}

3.下面来看JMSUIScrollViewTool

#import <Foundation/Foundation.h>

@interface JMSUIScrollViewTool : NSObject

+ (void)scrollViewScrollToTop;

@end
#import "JMSUIScrollViewTool.h"

@implementation JMSUIScrollViewTool

+ (void)scrollViewScrollToTop {
    UIWindow *window = [UIApplication sharedApplication].keyWindow;
    [self searchScrollViewInView:window];
}

+ (void)statusBarWindowClick {
    UIWindow *window = [UIApplication sharedApplication].keyWindow;
    [self searchScrollViewInView:window];
}

+ (BOOL)isShowingOnKeyWindow:(UIView *)view {
    UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
    CGRect newFrame = [keyWindow convertRect:view.frame fromView:view.superview];
    CGRect winBounds = keyWindow.bounds;
    BOOL intersects = CGRectIntersectsRect(newFrame, winBounds);
    return !view.isHidden && view.alpha > 0.01 && view.window == keyWindow && intersects;
}

+ (void)searchScrollViewInView:(UIView *)supView {
    for (UIScrollView *subView in supView.subviews) {
        if ([subView isKindOfClass:[UIScrollView class]] && [self isShowingOnKeyWindow:supView]) {
            CGPoint offset = subView.contentOffset;
            offset.y = -subView.contentInset.top;
            [subView setContentOffset:offset animated:YES];
        }
        
        [self searchScrollViewInView:subView];
    }
}

@end

相关文章

网友评论

      本文标题:iOS 获取电池栏的点击事件

      本文链接:https://www.haomeiwen.com/subject/iefmkqtx.html