NavigationBar
滑动时自动隐藏,和twitter
的效果一样。
向上滑动时,将会隐藏NavigationBar
,隐藏后可显示更多的内容。向下滑动时,将会显示导航栏。
最重要的是pod STNavigationBarAutoHide 后,你仅需一句代码便可实现此功能。
[self st_navigationBarAutoHideConfigureScrollView:yourScrollView];
Demo效果
|
|
---|
实现思路
滑动隐藏navigationBar
分为两部分,位置偏移及导航栏上内容透明度调整。
通过修改navigationBar的transform.ty
实现位置偏移。
递归获取_UINavigationBarContentView
的subviews
,再更改其alpha
,因导航标题无法更改alpha
,便采用了修改titleTextAttributes
中NSForegroundColorAttributeName
的alpha
来实现透明度调整。
实现细节
记录下开始拖动时的偏移量
- (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。谢谢~