在最近一個項目中碰到這樣一個場景,在被push進來的一個頁面設置導航欄透明,且要求控制對tableview組的頭視圖進行懸停顯示,nav隨著tableview偏移量改變透明度,當然這樣的需求確實不是什么難事,但是如果當前頁面繼續push一個不需要此類效果的頁面,當在返回當前頁面的時候就會出現一個坑,nav的展示很突兀,下面是直接上解決方法...ps:假設A頁面為需要設置透明,B頁面被Apush且不需要設置透明
A.m
首先在需要設置導航欄透明的頁面的viewDidload中寫上
self.title = @"Title";
[self.navigationController.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
self.navigationController.navigationBar.shadowImage = [UIImage new];
self.barImageView = self.navigationController.navigationBar.subviews.firstObject;
self.barImageView.alpha = 0;
//設置狀態欄
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
//設置標題顏色
self.navigationController.navigationBar.titleTextAttributes = @{NSForegroundColorAttributeName : [UIColor clearColor]};
在scrollViewDidScroll代理方法中
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat offset = scrollView.contentOffset.y;
//根據自己需要設置(136)的大小
CGFloat alpha = offset / 136;
_barImageView.alpha = alpha;
//記錄下當前的透明度,在返回當前頁面時需要
_alpha = alpha;
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithFloat:alpha] forKey:@"_alpha"];
//設置標題的透明度
self.navigationController.navigationBar.titleTextAttributes = @{NSForegroundColorAttributeName : [UIColor colorWithWhite:0 alpha:alpha]};
}
當前頁的viewWillAppear, viewDidAppear, viewWillDisappear
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.table.delegate = self;
BOOL isGesturePop = [[[NSUserDefaults standardUserDefaults] objectForKey:@"isGesturePop"] boolValue];
self.navigationController.navigationBar.titleTextAttributes = @{NSForegroundColorAttributeName : [UIColor colorWithWhite:0 alpha:_alpha]};
if (!isGesturePop) {
[UIView animateWithDuration:0.25 animations:^{
_barImageView.alpha = _alpha;
self.navigationController.navigationBar.titleTextAttributes = @{NSForegroundColorAttributeName : [UIColor colorWithWhite:0 alpha:_alpha]};
}];
}
}
-(void)viewDidAppear:(BOOL)animated {
//BOOL isGesturePop = [[[NSUserDefaults standardUserDefaults] objectForKey:@"isGesturePop"] boolValue];
// if (!isGesturePop) {
// _barImageView.alpha = _alpha;
// self.navigationController.navigationBar.titleTextAttributes = @{NSForegroundColorAttributeName : [UIColor colorWithWhite:0 alpha:_alpha]};
//}
[super viewDidAppear:animated];
}
-(void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
self.table.delegate = nil;
self.navigationController.navigationBar.titleTextAttributes = @{NSForegroundColorAttributeName : [UIColor blackColor]};
_barImageView.alpha = 1;
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:NO] forKey:@"isGesturePop"];
}
那么在我們需要push的下一個頁面需要什么操作呢,我們需要在這個頁面顯示正常的nav并且禁掉系統的手勢pop,自己寫一個pop手勢,以方便我們拿到pop滑動時的偏移量,在做的時候使用了兩個類,在最后會有源碼貼出
B.m 須遵守UIGestureRecognizerDelegate,并導入NavigationInteractiveTransition.h
全局變量
@property (nonatomic, strong) NavigationInteractiveTransition *navT;
viewDidLoad
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
UIGestureRecognizer *gesture = self.navigationController.interactivePopGestureRecognizer;
gesture.enabled = NO;
UIView *gestureView = gesture.view;
UIPanGestureRecognizer *popRecognizer = [[UIPanGestureRecognizer alloc] init];
popRecognizer.delegate = self;
popRecognizer.maximumNumberOfTouches = 1;
[gestureView addGestureRecognizer:popRecognizer];
_navT = [[NavigationInteractiveTransition alloc] initWithViewController:self.navigationController];
[popRecognizer addTarget:_navT action:@selector(handleControllerPop:)];
UIGestureRecognizerDelegate 代理方法gestureRecognizerShouldBegin
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
//記錄當前是是否是通過手勢滑動回去
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:YES] forKey:@"isGesturePop"];
/**
* 這里有兩個條件不允許手勢執行,1、當前控制器為根控制器;2、如果這個push、pop動畫正在執行(私有屬性)
*/
return self.navigationController.viewControllers.count != 1 && ![[self.navigationController valueForKey:@"_isTransitioning"] boolValue];
}
需要依賴的兩個類源碼
NavigationInteractiveTransition.h
#import <UIKit/UIKit.h>
@class UIViewController, UIPercentDrivenInteractiveTransition;
@interface NavigationInteractiveTransition : NSObject <UINavigationControllerDelegate>
- (instancetype)initWithViewController:(UIViewController *)vc;
- (void)handleControllerPop:(UIPanGestureRecognizer *)recognizer;
- (UIPercentDrivenInteractiveTransition *)interactivePopTransition;
@end
NavigationInteractiveTransition.m
#import "NavigationInteractiveTransition.h"
#import "PopAnimation.h"
@interface NavigationInteractiveTransition ()
@property (nonatomic, weak) UINavigationController *vc;
@property (nonatomic, strong) UIPercentDrivenInteractiveTransition *interactivePopTransition;
@property(nonatomic, strong) UIImageView *barImageView;
@end
@implementation NavigationInteractiveTransition
- (instancetype)initWithViewController:(UIViewController *)vc
{
self = [super init];
if (self) {
self.vc = (UINavigationController *)vc;
self.vc.delegate = self;
}
return self;
}
/**
* 我們把用戶的每次Pan手勢操作作為一次pop動畫的執行
*/
- (void)handleControllerPop:(UIPanGestureRecognizer *)recognizer {
/**
* interactivePopTransition就是我們說的方法2返回的對象,我們需要更新它的進度來控制Pop動畫的流程,我們用手指在視圖中的位置與視圖寬度比例作為它的進度。
*/
CGFloat progress = [recognizer translationInView:recognizer.view].x / recognizer.view.bounds.size.width;
[self.vc.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
self.vc.navigationBar.shadowImage = [UIImage new];
self.barImageView = self.vc.navigationBar.subviews.firstObject;
CGFloat alpha = [[[NSUserDefaults standardUserDefaults] objectForKey:@"_alpha"] floatValue];
self.barImageView.alpha = 1 - progress > alpha ? alpha : 1 - progress;
// NSLog(@"===progress==%.2f",progress);
/**
* 穩定進度區間,讓它在0.0(未完成)~1.0(已完成)之間
*/
progress = MIN(1.0, MAX(0.0, progress));
if (recognizer.state == UIGestureRecognizerStateBegan) {
/**
* 手勢開始,新建一個監控對象
*/
self.interactivePopTransition = [[UIPercentDrivenInteractiveTransition alloc] init];
/**
* 告訴控制器開始執行pop的動畫
*/
[self.vc popViewControllerAnimated:YES];
}
else if (recognizer.state == UIGestureRecognizerStateChanged) {
/**
* 更新手勢的完成進度
*/
[self.interactivePopTransition updateInteractiveTransition:progress];
}
else if (recognizer.state == UIGestureRecognizerStateEnded || recognizer.state == UIGestureRecognizerStateCancelled) {
/**
* 手勢結束時如果進度大于一半,那么就完成pop操作,否則重新來過。
*/
if (progress > 0.5) {
[self.interactivePopTransition finishInteractiveTransition];
self.barImageView.alpha = 0;;
}
else {
[self.interactivePopTransition cancelInteractiveTransition];
}
self.interactivePopTransition = nil;
}
}
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC {
/**
* 方法1中判斷如果當前執行的是Pop操作,就返回我們自定義的Pop動畫對象。
*/
if (operation == UINavigationControllerOperationPop)
return [[PopAnimation alloc] init];
return nil;
}
- (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController
interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController {
/**
* 方法2會傳給你當前的動畫對象animationController,判斷如果是我們自定義的Pop動畫對象,那么就返回interactivePopTransition來監控動畫完成度。
*/
if ([animationController isKindOfClass:[PopAnimation class]])
return self.interactivePopTransition;
return nil;
}
@end
PopAnimation.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface PopAnimation : NSObject <UIViewControllerAnimatedTransitioning>
@end
PopAnimation.m
#import "PopAnimation.h"
@interface PopAnimation ()
@property (nonatomic, strong) id <UIViewControllerContextTransitioning> transitionContext;
@end
@implementation PopAnimation
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
//這個方法返回動畫執行的時間
return 0.25;
}
/**
* transitionContext你可以看作是一個工具,用來獲取一系列動畫執行相關的對象,并且通知系統動畫是否完成等功能。
*/
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
/**
* 獲取動畫來自的那個控制器
*/
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
/**
* 獲取轉場到的那個控制器
*/
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
/**
* 轉場動畫是兩個控制器視圖時間的動畫,需要一個containerView來作為一個“舞臺”,讓動畫執行。
*/
UIView *containerView = [transitionContext containerView];
[containerView insertSubview:toViewController.view belowSubview:fromViewController.view];
NSTimeInterval duration = [self transitionDuration:transitionContext];
/**
* 執行動畫,我們讓fromVC的視圖移動到屏幕最右側
*/
[UIView animateWithDuration:duration animations:^{
fromViewController.view.transform = CGAffineTransformMakeTranslation([UIScreen mainScreen].bounds.size.width, 0);
}completion:^(BOOL finished) {
/**
* 當你的動畫執行完成,這個方法必須要調用,否則系統會認為你的其余任何操作都在動畫執行過程中。
*/
[transitionContext completeTransition:!transitionContext.transitionWasCancelled];
}];
}
- (void)animationDidStop:(CATransition *)anim finished:(BOOL)flag {
[_transitionContext completeTransition:!_transitionContext.transitionWasCancelled];
}
@end
ps:如果是自定義view設置nav或者是直接隱藏nav可以參考我的另一篇簡書http://www.lxweimin.com/p/b46166b05f02
備注:還有些許不足請各位見諒指正