UIScrollView回滚到顶部解决方案

UIScrollView回滚到顶部解决方案

  • 根据系统的不同分成两种情况
    • iOS9之前,如果页面上有两个UIScroolView,那么点击导航栏将没有反应
    • iOS9之后(包括iOS9),页面内多个UIScrollView,点击导航栏将根据不同情况,回滚不同的UIScroolView

iOS9之前的处理方案

  • 其实现在写这个方案意义不是很大,iOS11正式版很快就要发布了,对于系统的适配,iOS9-11就可以了,iOS8基本上可以放弃了
  • 先说一下思路
    • 需要拦截点击导航栏操作-使用UIWindow,在导航栏上面覆盖一层没有颜色的window,级别设置到最高
    • 拦截点击操作之后,遍历keyWindow上所有的子控件
    • 判断这个子控件是否是UIScrollView
    • 判断这个子控件是否显示在keyWindow上
    • 都满足之后将找到的子控件的contentOffset的y值设置为0即可
  • 这种方式的不足之处:如果有两个UIScrollView都在屏幕上面,都是垂直滚动,那么点击之后,两个都会回到顶部,想要解决个问题,就需要进一步判断是哪一个UIScrollView
// 顶部的window,覆盖在状态栏上面,点击之后将window上显示的scrollView滑动到顶部
#import 

@interface YWTopWindow : NSObject

+ (void)show;

+ (void)hide;

@end


#import "YWTopWindow.h"
#import "UIView+YWExtension.h"

@implementation YWTopWindow

static UIWindow *window_;

+ (void)initialize
{
    window_ = [[UIWindow alloc] init];
    window_.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 20);
    window_.backgroundColor = [UIColor clearColor];
    // 将级别设置为最高,这样就能盖住状态栏
    window_.windowLevel = UIWindowLevelAlert;
    [window_ addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(windowClick)]];
}

+ (void)show
{
    // iOS9之前,可以直接写window_.hidden = NO;
    // 在iOS9之后,直接写会报错,大概的意思就是:在程序启动结束后,window需要有一个根控制器
    // Application windows are expected to have a root view controller at the end of application launch
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        window_.hidden = NO;
    });
}

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


/**
 窗口点击
 */
+ (void)windowClick
{
    UIWindow *window = [UIApplication sharedApplication].keyWindow;
    [self searchScrollViewInView:window];
}

+ (void)searchScrollViewInView:(UIView *)superview
{
    for (UIScrollView *subView in superview.subviews) {
        // 如果是UIScrollView,滚动到最顶部
        if([subView isKindOfClass:[UIScrollView class]] && subView.isShowingOnKeyWindow){
            CGPoint offset = subView.contentOffset;
            offset.y = - subView.contentInset.top;
            [subView setContentOffset:offset animated:YES];
        }
        // 不是UIScrollView或者不是显示在keyWindow上,继续寻找
        [self searchScrollViewInView:subView];
    }
}
@end

// 写在UIView的分类中的方法
// 判断一个控件是否显示在主窗口上面
- (BOOL)isShowingOnKeyWindow
{
    // 主窗口
    UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
    
    // 以主窗口左上角为坐标原点,计算self的矩形框
    CGRect newFrame = [keyWindow convertRect:self.frame fromView:self.superview];
    CGRect winBounds = keyWindow.bounds;
    
    // 主窗口的bounds和self的矩形框是否有重叠
    BOOL intersects = CGRectIntersectsRect(newFrame, winBounds);
    
    return !self.isHidden && self.alpha > 0.01 && self.window == keyWindow && intersects;
}

iOS9之后(包含iOS9)

  • 页面上两个UIScrollView,类似今日头条的布局,第一个横向滑动,第二个也是横向滑动,但是里面有好多UITableView
    • 这种可以不用管,系统会自动将当前显示的UITableView滑动到顶部
  • 页面上有两个UIScrollView,都是垂直滚动,两个都显示在页面上
    • 左右分布,那么点击导航栏位置在那个控件上方,滚动那个UIScrollView
    • 上下分布,点击导航栏时,上方的控件在初始位置,那么滚动下方的控件,如果上方的有滚动过,那么就会将上方的UIScrollView滚动到最上方

你可能感兴趣的:(UIScrollView回滚到顶部解决方案)