iOS点击状态栏回到顶部底层实现原理

iOS点击状态栏回到顶部底层实现原理

    在iOS开发中,苹果其实已经帮你实现了点击状态栏回到顶部这个功能,但我们在开发中会遇到点击不能回到顶部.其实这都和

ScrollView中的一个属性scrollsToTop有关,我们先看看苹果关于这个属性的解释:

// 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.

其实根据苹果说的,如果当前窗口上,如果你不止一个scrollView的话,因为苹果不知道当你点击状态栏的时候,不知道让哪一个scrollView回到顶部,就会默认把这个属性赋值为NO;

   其实解决方法很简单,你确定好点击状态栏你需要哪个scrollView回到顶部,把他的属性赋值为YES,其他的scrollView 的这个属性赋值为NO.这样就可以利用系统的这个功能.

  scrollsToTop属性,苹果内部实现原理:

思想: 苹果的底层就是使用递归,遍历当前窗口上所有的View,找到那个scrollView,并让它回到顶部,这也就解释了,为什么当前窗口上,如果有几个scrollView的话,scrollsToTop属性,会失效.

假设我现在需要拿到窗口上的这个tableView,实现要它回到顶部

1.获取主窗口,调用递归函数

// 遍历窗口所有的子控件,看有没有tableView
    UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
    
    // 获取窗口的所有子控件fetchTableView就是递归方法
    UITableView *tableView = [self fetchTableView:keyWindow];

2.递归方法实现

 若是找到想要找到的tableView,就返回,否则就返回空

- (UITableView *)fetchTableView:(UIView *)view
{
    //以当前view为父控件,查找子控件
    for (UIView *childView in view.subviews) {
        
        if ([childView isKindOfClass:[UITableView class]]) {
            return (UITableView *)childView;
        }
        
         return  [self fetchTableView:childView];

    }
    
    return nil;
}

还有一种方法,在状态栏的一样的位置外面加一个window窗口

static XMGStatusWindow *_statusWindow;

+ (void)show
{
    XMGStatusWindow *statusWindow = [[XMGStatusWindow alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 20)];
    //将这个window优先级设为最高
    statusWindow.windowLevel = UIWindowLevelAlert;
    statusWindow.backgroundColor = [UIColor clearColor];
    statusWindow.rootViewController = [[UIViewController alloc] init];
    statusWindow.hidden = NO;
    _statusWindow = statusWindow;
    
    // 当程序有多个窗口的时候,状态栏自动隐藏
    // 恢复状态栏显示
    [UIApplication sharedApplication].statusBarHidden = NO;
    
}

注: 在iOS10之后,苹果为了提高collectionView性能,苹果加了预加载,会同时在窗口上加载三个collectionView的cell,也就会导致你会在窗口上拿到三个cell.解决这个问题只需设置:

collectionView.prefetchingEnabled = NO;