iOS开发基础55-利用 UIWindow 实现快速滚动到界面顶部

在现代应用中,用户体验是我们关注的重点之一,为了提升用户的使用体验,我们可以在状态栏添加一个看不见(透明)的 UIWindow 并附加一个按钮,实现点击按钮时快速滚动到当前界面的顶部。这篇文章将详细介绍如何利用 UIWindow 实现该功能,并封装成一个工具类供外部使用。

一、UIWindow 的基本介绍

在 iOS 中,UIWindow 是用于管理和协调应用程序的主要可视内容的对象,并且 UIWindow 可以设置多个层级,层级越高,显示的位置越上面。

  • UIWindowLevel
    • UIWindowLevelNormal:普通窗口层次。
    • UIWindowLevelStatusBar:状态栏窗口层次。
    • UIWindowLevelAlert:警告窗口层次,该层级最高。

二、用 UIWindow 实现滚动到顶部功能

1. 创建一个新的 UIWindow

首先,我们需要创建一个新的窗口,并设置它的层级为最高的 UIWindowLevelAlert。将这个窗口添加到状态栏位置并设置为透明,同时设置一个点击事件:

@interface AppDelegate ()
@property (nonatomic, strong) UIWindow *topWindow;
@end

@implementation AppDelegate

- (UIWindow *)topWindow {
    if (_topWindow == nil) {
        _topWindow = [[UIWindow alloc] init];
        _topWindow.windowLevel = UIWindowLevelAlert;
        _topWindow.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 20);
        _topWindow.backgroundColor = [UIColor clearColor];
        // 默认是隐藏的,要显示必须手动设置为NO
        _topWindow.hidden = NO;
        // 添加点击事件
        [_topWindow addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(topWindowClick)]];
    }
    return _topWindow;
}

2. 遍历每一个窗口

当用户点击状态栏上的透明窗口时,触发点击事件,遍历所有窗口:

- (void)topWindowClick {
    NSArray *windows = [UIApplication sharedApplication].windows;
    for (UIWindow *window in windows) {
        [self searchSubviews:window];
    }
}

3. 遍历窗口下的所有子控件

递归遍历每个窗口的子控件,找到 UIScrollView 类的控件,判断其是否在窗口上(是否相交),如果在则滚动到顶部:

- (void)searchSubviews:(UIView *)superview {
    for (UIView *subview in superview.subviews) {
        [self searchSubviews:subview];
        
        if (![subview isKindOfClass:[UIScrollView class]]) continue;
        
        UIScrollView *scrollView = (UIScrollView *)subview;
        CGRect scrollViewRect = [scrollView convertRect:scrollView.bounds toView:scrollView.window];
        CGRect windowRect = scrollView.window.bounds;
        
        if (!CGRectIntersectsRect(scrollViewRect, windowRect)) continue;
        
        CGPoint offset = scrollView.contentOffset;
        offset.y = -scrollView.contentInset.top;
        [scrollView setContentOffset:offset animated:YES];
    }
}

4. 程序激活时主动调用懒加载

为了确保窗口在应用激活时能够显示,我们需要在 applicationDidBecomeActive 方法中调用懒加载:

- (void)applicationDidBecomeActive:(UIApplication *)application {
    [self topWindow];
}

三、封装工具类供外部使用

为了方便在项目中多次使用,可以将上述实现封装成一个工具类。

@interface TopWindowHelper : NSObject

+ (void)setupTopWindow;

@end

@implementation TopWindowHelper

static UIWindow *topWindow;

+ (void)initialize {
    [self setupTopWindow];
}

+ (void)setupTopWindow {
    if (topWindow == nil) {
        topWindow = [[UIWindow alloc] init];
        topWindow.windowLevel = UIWindowLevelAlert;
        topWindow.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 20);
        topWindow.backgroundColor = [UIColor clearColor];
        topWindow.hidden = NO;
        [topWindow addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(topWindowClick)]];
    }
}

+ (void)topWindowClick {
    NSArray *windows = [UIApplication sharedApplication].windows;
    for (UIWindow *window in windows) {
        [self searchSubviews:window];
    }
}

+ (void)searchSubviews:(UIView *)superview {
    for (UIView *subview in superview.subviews) {
        [self searchSubviews:subview];
        
        if (![subview isKindOfClass:[UIScrollView class]]) continue;
        
        UIScrollView *scrollView = (UIScrollView *)subview;
        CGRect scrollViewRect = [scrollView convertRect:scrollView.bounds toView:scrollView.window];
        CGRect windowRect = scrollView.window.bounds;
        
        if (!CGRectIntersectsRect(scrollViewRect, windowRect)) continue;
        
        CGPoint offset = scrollView.contentOffset;
        offset.y = -scrollView.contentInset.top;
        [scrollView setContentOffset:offset animated:YES];
    }
}

@end

在 AppDelegate 中调用工具类,以确保窗口在应用激活时能够显示:

- (void)applicationDidBecomeActive:(UIApplication *)application {
    [TopWindowHelper setupTopWindow];
}

结语

通过利用 UIWindowUIScrollView 的结合,我们可以轻松实现点击状态栏快速滚动到页面顶部的功能。这个功能在用户体验上有很大的提升,非常适合在新闻类、社交类等应用中使用。封装成工具类后,可以方便地在项目中多次调用,促进代码的重用。

posted @ 2015-09-25 01:14  Mr.陳  阅读(1059)  评论(0编辑  收藏  举报