zoukankan      html  css  js  c++  java
  • 点击状态栏回到顶部的功能失效的解决办法

     

    在我们IOS开发中,UIScrollView自带有点击顶部状态栏自动返回顶部的效果,不过这个效果是有约束条件的:

    // 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 `shouldScrollViewScrollToTop`, and it is not already at the top.
    // 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.
    @property(nonatomic) BOOL  scrollsToTop __TVOS_PROHIBITED;          // default is YES.

    从上面分析我们可以得出结论:我们必须保证窗口上scrollsToTop == YESScrollView(及其子类)同一时间内有且只有一个。这一样才能保证点击statusBar,该唯一存在的ScrollView能自动回到顶部。即这个手势只能作用在一个scrollView上,当发现多个时,手势将会失效。

    在实际应用中,我们可能会有多个scrollView(包含UITableView/UICollectionView),如汽车之家、网易新闻、爱奇艺等等应用,这时候,系统默认的点击状态栏返回到顶部效果就会失效,

    如何保证苹果自带的该功能一直好使呢?

    解决方案一:

    当前显示哪个tableView,哪个的scrollsToTop就设置为YES,其余的设置为NO;

    解决方案二:自己实现

    初级思路:在statusBar的区域添加一个遮盖,监听遮盖的点击事件 (用监听也可以就是有点low  下面有更好的方法用递归)

    1.首先我们想到用UIView来做这个遮盖。但是,在这里我们使用UIView是着不住statusBar的,UIView会一直在statusBar的下面,所以不能接收点击事件。因为statusBar其实是一个UIWindow,且优先级高于下面的keyWindow。所以,添加的UIView会在statusBar的下面。

    2.由于优先级的关系,我们可以用一个UIWindow来做遮盖,设置遮盖window的优先级高于statusBar即可。当然,设置最高优先级(UIWindowLevelAlert)肯定是可以的。然后给遮盖Window添加一个点击事件,背景色设置透明即可

    代码如下

    #import "AppDelegate.h"
    
    @interface AppDelegate ()
    @property(strong, nonatomic) UIWindow *coverStatusBarWindow; // 覆盖在statusBar上的透明窗口
    @end

    在代理方法中添加 coverStatusBarWindow 

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        
        //添加coverStatusBarWindow 并让其显示出来
        UIWindow * coverStatusBarWindow =[[UIWindow alloc]initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 20)];
        coverStatusBarWindow.rootViewController = [[UIViewController alloc]init];
        coverStatusBarWindow.backgroundColor = [UIColor clearColor];
        coverStatusBarWindow.windowLevel = UIWindowLevelStatusBar+1;
        [coverStatusBarWindow makeKeyAndVisible];
        self.coverStatusBarWindow = coverStatusBarWindow;
        
        UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(coverWindowOnClicked)];
        [self.coverStatusBarWindow addGestureRecognizer:tap];
    
    
        self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
        self.window.backgroundColor = [UIColor grayColor];
        // 创建一个控制器
        UIViewController *vc = [[UIViewController alloc] init];
        self.window.rootViewController = vc;
        self.window.windowLevel = UIWindowLevelNormal;
        // 让UIwindow成为keyWindow(主窗口),并且可见
        [self.window1 makeKeyAndVisible];
        // 给UIwindow添加一个输入框
        UITextField *tf = [[UITextField alloc] init];
        tf.frame = CGRectMake(10, 64, 100, 20);
        tf.borderStyle = UITextBorderStyleRoundedRect;
        [self.window addSubview:tf];
    
        return YES;
    
    
    }
    
    //发通知可以让其他的页面监听到状态栏的点击
    - (void)coverWindowOnClicked{
        [[NSNotificationCenter defaultCenter]postNotificationName:@"onClickedStatusBarNotification" object:self userInfo:nil];
    }

    在其他控制器里面监听

    - (void)viewDidLoad {
        [super viewDidLoad];
    
        [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(onClickedStatusBar:) name:@"kOnClickedStatusBarNotification" object:nil];
        
    }
    
    - (void)onClickedStatusBar:(NSNotification *)noti{
        //让当前所显示的tableView 回到最顶部 (偏移量看情况设定哦)
        self.currentTableView.contentOffset = CGPointMake(0, 0);
    }
    
    - (void)dealloc{
        [[NSNotificationCenter defaultCenter]removeObserver:self];
    }

    想移除coverStatusBarWindow 将其赋值为空

        self.coverStatusBarWindow = nil;

    这里面可以将  coverStatusBarWindow 抽出来封装一下  提供 创建sharedCoverStatusBarWindow的方法 和show  和 dismiss 的方法

    自己可以去试一下 

    最终思路:

    1).创建一个UIWindow,背景颜色设置成透明色,frame设置成statusBar的frame,覆盖掉statusBar

    2).给这个window添加一个点击的手势,点击这个window就遍历keyWindow中所有的子控件,取出当前显示在眼前的UIScrollView,将其滑动到顶部

    代码如下(封装了一下)

    .h文件

    //
    //  JHStatusBarScrollsToTopManager.h
    //  TestQuestion
    //
    //  Created by  Mark on 2016/10/28.
    //  Copyright © 2016年 Mark. All rights reserved.
    //  点击头部的状态栏,当前显示在眼前的scrollView就会移动到最初的位置,用于解决有嵌套多个scrollView 系统scrollsToTop 禁用问题
    
    #import <Foundation/Foundation.h>
    
    @interface JHStatusBarScrollsToTopManager : NSObject
    
    
    /**
     生效
     */
    + (void)becomeEffective;
    
    /**
     失效
     */
    + (void)disabled;
    
    @end

    .m文件

    //
    //  JHStatusBarScrollsToTopManager.m
    //  TestQuestion
    //
    //  Created by Mark on 2016/10/28.
    //  Copyright © 2016年 淡蓝. All rights reserved.
    //
    #import <UIKit/UIKit.h>
    #import "JHStatusBarScrollsToTopManager.h"
    
    @implementation JHStatusBarScrollsToTopManager
    
    static UIWindow *statusBarWindow;
    
    + (void)becomeEffective {
        if (statusBarWindow == nil) {
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                statusBarWindow = [[UIWindow alloc] init];
                statusBarWindow.frame = [UIApplication sharedApplication].statusBarFrame;
                statusBarWindow.backgroundColor = [UIColor clearColor];
                statusBarWindow.windowLevel = UIWindowLevelStatusBar+1;
                [statusBarWindow addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(topWindowClick)]];
                statusBarWindow.hidden = NO;
            });
        }
        
        else {
            statusBarWindow.hidden = NO;
        }
    }
    
    + (void)disabled {
        statusBarWindow.hidden = YES;//不用在赋值为空了 为了下次不用再次创建
    }
    
    - (void)topWindowClick {
        // 用递归的思想找到所有的UIScrollView
        UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
        [self searchAllScrollViewsInView:keyWindow];
    }
    
    /**
     *  找到view里面的所有UIScrollView 并 取出当前显示在眼前的UIScrollView将其滑动到顶部
     */
    - (void)searchAllScrollViewsInView:(UIView *)view {
        
        // 这个for循环可以保证所有子控件都能传进来
        for (UIView *subview in view.subviews) {
            [self searchAllScrollViewsInView:subview];
        }
        
        // 如果不是UIScrollView,直接返回
        if (![view isKindOfClass:[UIScrollView class]]) return;
        
        //如果是scrollView则进行如下的处理
        UIScrollView *scrollView = (UIScrollView *)view;
        
        CGRect scrollViewRect = [scrollView convertRect:scrollView.bounds toView:nil];
        CGRect keyWindowRect = [UIApplication sharedApplication].keyWindow.bounds;
        
        // 如果scrollView的矩形框 跟 keywindow 没有重叠,(表示不是显示在眼前的UIScrollView)直接返回
        if (!CGRectIntersectsRect(scrollViewRect, keyWindowRect)) return;
        
        // 若scrollView与keyWindow重叠(是显示在眼前的UIScrollView)让UIScrollView滚动到最前面
        [scrollView scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:YES];
        
    }
    
    
    @end
  • 相关阅读:
    使用FastJson parseObject方法时,json字符串解析成对象后,部分属性丢失问题处理
    IDEA启动Tomcat时 , 报错提示:this web application instance has been stopped already
    Servlet基本概念记录(随笔...不完全...)
    有关MacBook的简单操作收集
    Elasticsearch 相关资料收集
    redis入门
    整理最近面试遇到的一些问题
    java中的一些概念整理
    windows相关操作
    java基础知识优秀博客整理
  • 原文地址:https://www.cnblogs.com/junhuawang/p/6003191.html
Copyright © 2011-2022 走看看