iOS NavigationBar滑动时自动隐藏

NavigationBar滑动时自动隐藏,和twitter的效果一样。

向上滑动时,将会隐藏NavigationBar,隐藏后可显示更多的内容。向下滑动时,将会显示导航栏。

最重要的是pod STNavigationBarAutoHide 后,你仅需一句代码便可实现此功能。

[self st_navigationBarAutoHideConfigureScrollView:yourScrollView];

Demo效果

实现思路

滑动隐藏navigationBar分为两部分,位置偏移及导航栏上内容透明度调整。

通过修改navigationBar的transform.ty实现位置偏移。

递归获取_UINavigationBarContentViewsubviews,再更改其alpha,因导航标题无法更改alpha,便采用了修改titleTextAttributesNSForegroundColorAttributeNamealpha来实现透明度调整。

实现细节

记录下开始拖动时的偏移量

- (void)st_scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    CGFloat contentOffsetY = scrollView.contentOffset.y + scrollView.adjustedContentInset.top;
    [self st_setBeginContentOffsetY:contentOffsetY];
}

滑动时,判断是否隐藏

- (void)st_scrollViewDidScroll:(UIScrollView *)scrollView {
    CGFloat contentOffsetY = scrollView.contentOffset.y + scrollView.adjustedContentInset.top;
    
    //滑动距离,大于0时,向上滑动;小于0时,向下滑动。
    CGFloat scrollOffsetY = contentOffsetY - [self st_beginContentOffsetY];
    //导航栏高度
    CGFloat navigationBarHeight = self.navigationController.navigationBar.frame.size.height;
    //导航栏是否隐藏
    bool navigationBarHidden = [self st_navigationBarHidden];
    
    //向上滑动且导航未隐藏
    if (scrollOffsetY > 0 && !navigationBarHidden) {
        //滑动距离(下方用于计算动画进度)不大于导航高度
        scrollOffsetY = MIN(navigationBarHeight, scrollOffsetY);
        //滑动距离等于导航高度时,导航栏完全隐藏
        if (scrollOffsetY == navigationBarHeight) {
            [self st_setNavigationBarHidden:true];
            [self st_setBeginContentOffsetY:contentOffsetY];
        }
        //计算动画进度,更改导航内容透明度
        CGFloat alpha = (navigationBarHeight - scrollOffsetY) / navigationBarHeight;
        [self updateNavigationBarAlpha:alpha];
        
        //更改导航位置偏移量
        self.navigationController.navigationBar.transform = CGAffineTransformMakeTranslation(0, -scrollOffsetY);
        
    } 
    //向下滑动 并且 偏移量小于导航高度 并且 导航已隐藏时,显示导航
    else if (scrollOffsetY < 0 && contentOffsetY < navigationBarHeight && navigationBarHidden) {
        [self st_showNavigationBarWithAnimated:true];
    }
}
   

拖动结束时,判断是否隐藏

- (void)st_scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
    //速度
    CGFloat velocityY = velocity.y;
    
    //速度小于0时,向下拖动,显示导航栏
    if (velocityY < 0) {
        [self st_showNavigationBarWithAnimated:true];
    }
    
    //速度为0时
    else if (velocityY == 0) {
        CGFloat contentOffsetY = scrollView.contentOffset.y + scrollView.adjustedContentInset.top;
        CGFloat scrollOffsetY = contentOffsetY - [self st_beginContentOffsetY];
        CGFloat navigationBarHeight = self.navigationController.navigationBar.frame.size.height;
        //偏移量小于导航高度时,显示导航栏
        if (contentOffsetY < navigationBarHeight) {
            [self st_showNavigationBarWithAnimated:true];
            
        } else {
            //导航当前位置偏移
            CGFloat transformTy = self.navigationController.navigationBar.transform.ty;
            //当导航已隐藏,或已显示时,return
            if (transformTy == 0 && transformTy == -navigationBarHeight) return;
            if (scrollOffsetY <= 0) return;
            
            //处理向上拖动,导航未完全隐藏时,拖动结束情况
            if (transformTy >= -navigationBarHeight / 2.0) {
                [self st_showNavigationBarWithAnimated:true];
    
            } else {
                [self st_hideNavigationBarWithAnimated:true];
            }
        }
    }
}

用category封装

新建UIViewController+NavigationBarAutoHide类

配置scrollView

- (void)st_navigationBarAutoHideConfigureScrollView:(UIScrollView *)scrollView {
    if (scrollView == nil) {
        NSLog(@"[ST NavigationBarAutoHide Error] scrollView cannot be nil!");
        return;
    }
    if (self.navigationController == nil) {
        NSLog(@"[ST NavigationBarAutoHide Error] self.navigationController cannot be nil!");
        return;
    }
    if (scrollView.delegate == nil) {
        scrollView.delegate = self;
    }
    //自动隐藏默认设为可用
    [self st_setNavigationBarAutoHideEnabled:true];
    
    //交换scrollView delegate上方用到的三个方法
    [self st_swizzleScrollViewDelegateMethod];
}

交换scrollView delegate用到的三个方法

- (void)st_swizzleScrollViewDelegateMethod {
    [self st_swizzleScrollViewMethodWithOriginalSelector:@selector(scrollViewDidScroll:) swizzleSelector:@selector(st_scrollViewDidScroll:)];
    [self st_swizzleScrollViewMethodWithOriginalSelector:@selector(scrollViewWillBeginDragging:) swizzleSelector:@selector(st_scrollViewWillBeginDragging:)];
    [self st_swizzleScrollViewMethodWithOriginalSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:) swizzleSelector:@selector(st_scrollViewWillEndDragging:withVelocity:targetContentOffset:)];
}

- (void)st_swizzleScrollViewMethodWithOriginalSelector:(SEL)originalSelector swizzleSelector:(SEL)swizzleSelector {
    Method originalMethod = class_getInstanceMethod([self class], originalSelector);
    Method swizzledMethod = class_getInstanceMethod([self class], swizzleSelector);
    
    //若未实现代理方法,则先添加代理方法
    BOOL didAddOriginalMethod = st_addMethod([self class], originalSelector, originalMethod);
    BOOL didAddSwizzledMethod = st_addMethod([self class], swizzleSelector, swizzledMethod);
    
    if (didAddOriginalMethod) {
        Method newOriginalMethod = class_getInstanceMethod([self class], originalSelector);
        if (originalMethod == newOriginalMethod) {
            didAddOriginalMethod = false;
        }
    }

    if (didAddSwizzledMethod) {
        Method newSwizzledMethod = class_getInstanceMethod([self class], swizzleSelector);
        if (swizzledMethod == newSwizzledMethod) {
            didAddSwizzledMethod = false;
        }
    }

    //如果两个方法都存在,则表示已交换过
    if (!didAddOriginalMethod && !didAddSwizzledMethod) return;
    
    st_swizzleSelector([self class], originalSelector, swizzleSelector);
}

static inline BOOL st_addMethod(Class theClass, SEL selector, Method method) {
    return class_addMethod(theClass, selector,  method_getImplementation(method),  method_getTypeEncoding(method));
}

static inline void st_swizzleSelector(Class theClass, SEL originalSelector, SEL swizzledSelector) {
    Method originalMethod = class_getInstanceMethod(theClass, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(theClass, swizzledSelector);
    method_exchangeImplementations(originalMethod, swizzledMethod);
}

最后

更多细节可查看源码,STNavigationBarAutoHide,支持pod。

如果你有问题,可以在github上给我提Issue或者在下方评论。

如果此项目有帮助到你,请给我一个star。谢谢~

你可能感兴趣的:(iOS NavigationBar滑动时自动隐藏)