iOS-点击状态栏自动回到顶部功能实现详解

状态栏(statusBar)点击自动回到顶部效果,旨在为用户在浏览界面时提供便利,点击状态栏能够快速回到界面顶部,所以主要针对可以滚动的UIScrollView和其子类UITableVIew和UICollectionView。这里将从以下几个方面实现该功能。


1.苹果自带功能

分析:

  • 首先,苹果自己已经提供了该功能,往上滑动tabView,点击statusBar,tableView会自动回到初始位置。如下图所示,此时点击statusBar,屏幕最上方显示的将是第一个cell。在一个控制器上添加一个tabView,那么默认点击statusBar是可以自动回到顶部的。


    iOS-点击状态栏自动回到顶部功能实现详解_第1张图片
    Snip20160717_10.png
  • 既然苹果已经提供了该功能,我们直接拿来用就好了,干嘛还要自己实现呢?
  • 其实不然,在一些情况下该功能是无效的。比如,在窗口上同时存在两个或两个以上UIScrollView或其子类时。例如,将上面的tabView先添加到一个scrollView上,然后再将该scrollView添加到控制器的View上,此时点击statusBar,tabView不能自动回到顶部。
  • 因为,该效果是否有效,与 scrollsToTop属性相关。查看官方文档,以下几点值得注意:
    • 1.默认情况下scrollsToTop是为YES的,只有当该属性为YES时,点击statusBar才有效。
  • 2.该效果是让距离statusBar最近的ScrollView自动回到顶部
  • 3.在iPhone屏幕上方,当存在多个ScrollView(或其子类),如果scrollsToTop= YES 的ScrollView超过一个,所有ScrollView都不会响应statusBar的点击。
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.

小结:

从上面分析我们可以得出结论:我们必须保证窗口上scrollsToTop == YES的ScrollView(及其子类)同一时间内有且只有一个。这一样才能保证点击statusBar,该唯一存在的ScrollView能自动回到顶部。


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

解决办法:我们希望回到顶部的ScrollView的scrollsToTop =YES,其他scrollsToTop = NO。

  • 有时,为了满足某种需求,我们在一个scrollView上面会添加多个TabView,实现上下滑动显示cell的不同内容,左右滑动可以切换不同的tabView,这时点击statusBar是没有效果的。因为所有的scrollView的scrollsToTop =YES。要想展示每个TableView时,点击statusBar都有效,必须让除了展示在最上面的TabView以外的所有的ScrollView的scrollsToTop =NO。这就需要去判断,到底显示的是哪一个TabView。参考代码如下:
1.让最下面的scrollView,scrollsToTop =NO。其他TableView都是该scrollView的子类。
2.遍历判断
  // 控制scrollView的scrollsToTop属性
    for (NSInteger i = 0; i < self.childViewControllers.count; i++) {
        UIViewController *childVc = self.childViewControllers[i];
        
        // 如果控制器的view没有被创建,跳过
        if (!childVc.isViewLoaded) continue;
        
        // 如果控制器的view不是scrollView,就跳过
        if (![childVc.view isKindOfClass:[UIScrollView class]]) continue;
        
        // 如果控制器的view是scrollView
        UIScrollView *scrollView = (UIScrollView *)childVc.view;
        scrollView.scrollsToTop = (i == index);
    }


2.自己实现

在statusBar的区域添加一个遮盖,监听遮盖的点击事件。

UIView

  • 首先我们想到用UIView来做这个遮盖。但是,在这里我们使用UIView是着不住statusBar的,UIView会一直在statusBar的下面,所以不能接收点击事件。因为statusBar其实是一个UIWindow,且优先级高于下面的keyWindow。所以,添加的UIView会在statusBar的下面。
iOS-点击状态栏自动回到顶部功能实现详解_第2张图片
Snip20160717_14.png

UIWindow

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


    iOS-点击状态栏自动回到顶部功能实现详解_第3张图片
    Snip20160717_15.png

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        
        UIWindow * coverWindow =[[UIWindow alloc]initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 20)];
        self.coverWindow = coverWindow;
        coverWindow.hidden = NO;
        coverWindow.backgroundColor = [UIColor redColor];
        coverWindow.windowLevel = UIWindowLevelAlert;
        //添加手势
        UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(coverWindowClick)];
        [self.coverWindow addGestureRecognizer:tap];
    });

- (void)coverWindowClick {
   [UIView animateWithDuration:0.5 animations:^{
       
       self.tableView.contentOffset =  CGPointMake(0, 0);
   }];
}

AppDelegate中直接监听statusBar的点击

  • 在AppDelegate中实现touchesBegan:方法
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event  {
     if ([touches.anyObject locationInView:nil].y > 20) return;
    [[NSNotificationCenter defaultCenter]postNotificationName:@"click" object:nil];
    
}
  • 接收通知,修改tabView的contentOffset
- (void)coverWindowClick {
   [UIView animateWithDuration:0.5 animations:^{
       
       self.tableView.contentOffset =  CGPointMake(0, 0);
   }];
}

你可能感兴趣的:(iOS-点击状态栏自动回到顶部功能实现详解)