點擊狀態欄回到頂部的功能失效的解決辦法


 

在我們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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM