1. 要實現(xiàn)以下這些效果都非常簡單
2. 廢話不多說罩润,先看看實現(xiàn)效果
3. 下面告訴你我為什么說實現(xiàn)這些效果非常簡單
比如說要實現(xiàn)螞蟻森林的導(dǎo)航欄效果(有以下幾個需求):
- 剛進(jìn)入導(dǎo)航欄透明、兩邊按鈕和文字都是白色翼馆、狀態(tài)欄也是白色
- 向上滾動后導(dǎo)航欄背景由透明逐漸變成白色
- 當(dāng)超過某一點后割以,標(biāo)題變成黑色、狀態(tài)欄變成黑色应媚、兩邊按鈕變成藍(lán)色
實現(xiàn)步驟:
3.1. 實現(xiàn)剛進(jìn)入導(dǎo)航欄透明严沥、兩邊按鈕和文字都是白色、狀態(tài)欄也是白色
- (void)viewDidLoad
{
[super viewDidLoad];
// 設(shè)置導(dǎo)航欄顏色為白色
[self wr_setNavBarBarTintColor:[UIColor whiteColor]];
// 設(shè)置導(dǎo)航欄透明度為0
[self wr_setNavBarBackgroundAlpha:0];
}
3.2. 實現(xiàn)剩下兩個需求
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat offsetY = scrollView.contentOffset.y;
if (offsetY > NAVBAR_COLORCHANGE_POINT)
{
CGFloat alpha = (offsetY - NAVBAR_COLORCHANGE_POINT) / NAV_HEIGHT;
// 向上滾動后導(dǎo)航欄背景由透明逐漸變成白色
[self wr_setNavBarBackgroundAlpha:alpha];
if (alpha > 0.5) {
// 當(dāng)超過某一點后中姜,兩邊按鈕變成藍(lán)色
[self wr_setNavBarTintColor:[UIColor colorWithRed:0 green:0.478431 blue:1 alpha:1.0]];
// 標(biāo)題變成黑色
[self wr_setNavBarTitleColor:[UIColor blackColor]];
// 狀態(tài)欄變成黑色
[self wr_setStatusBarStyle:UIStatusBarStyleDefault];
} else {
// 當(dāng)沒有超過某點消玄,上面屬性還原
[self wr_setNavBarTintColor:[UIColor whiteColor]];
[self wr_setNavBarTitleColor:[UIColor whiteColor]];
[self wr_setStatusBarStyle:UIStatusBarStyleLightContent];
}
}
else
{
[self wr_setNavBarBackgroundAlpha:0];
[self wr_setNavBarTintColor:[UIColor whiteColor]];
[self wr_setNavBarTitleColor:[UIColor whiteColor]];
[self wr_setStatusBarStyle:UIStatusBarStyleLightContent];
}
}
3.3. 發(fā)現(xiàn)沒有,改變相關(guān)屬性只要一句代碼就完全搞定了6摺t婀稀!
// 一行代碼搞定導(dǎo)航欄顏色
[self wr_setNavBarBarTintColor:[UIColor whiteColor]];
// 一行代碼搞定導(dǎo)航欄透明度
[self wr_setNavBarBackgroundAlpha:alpha];
// 一行代碼搞定導(dǎo)航欄兩邊按鈕顏色
[self wr_setNavBarTintColor:[UIColor whiteColor]];
// 一行代碼搞定導(dǎo)航欄上標(biāo)題顏色
[self wr_setNavBarTitleColor:[UIColor whiteColor]];
// 一行代碼搞定狀態(tài)欄是 default 還是 lightContent
[self wr_setStatusBarStyle:UIStatusBarStyleLightContent];
3.4. 說了這么多携龟,看看幾句代碼能否實現(xiàn)我們需要的效果吧
3.5. 有人可能會問:這只是在一個界面里面兔跌,但是涉及到push、pop峡蟋、右滑手勢怎么辦呢坟桅?
答:沒關(guān)系,我已經(jīng)給你處理好了蕊蝗,你不用寫一句代碼=雠摇!匿又!那么看看效果吧
4. 好了方灾,說了這么多接下來看看如何實現(xiàn)的吧
4.1 實現(xiàn)導(dǎo)航欄透明漸變就很簡單了建蹄,網(wǎng)上一找一大堆碌更,大部分都是通過加一層的方法來實現(xiàn)(在這里就是加一個view到navigationBar上)
// set navigationBar barTintColor
- (void)wr_setBackgroundColor:(UIColor *)color
{
if (self.backgroundView == nil)
{
// add a image(nil color) to _UIBarBackground make it clear
[self setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
self.backgroundView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.bounds), kWRNavBarBottom)];
// _UIBarBackground is first subView for navigationBar
[self.subviews.firstObject insertSubview:self.backgroundView atIndex:0];
}
self.backgroundView.backgroundColor = color;
}
// set _UIBarBackground alpha (_UIBarBackground subviews alpha <= _UIBarBackground alpha)
- (void)wr_setBackgroundAlpha:(CGFloat)alpha
{
UIView *barBackgroundView = self.subviews.firstObject;
barBackgroundView.alpha = alpha;
}
4.2 你以為就這樣結(jié)束了嗎裕偿,來看看下面的問題,當(dāng)由透明的導(dǎo)航欄右滑到不透明的導(dǎo)航欄看看會出現(xiàn)什么情況痛单?
4.3 處理右滑返回手勢問題
我們都知道嘿棘,導(dǎo)航欄是屬于導(dǎo)航控制器的,一個導(dǎo)航欄不可能出現(xiàn)兩個顏色旭绒,那么右滑突兀怎么解決呢鸟妙?兩個方法,一個方法是自定義導(dǎo)航欄(后面會說)挥吵,另一個方法是改變導(dǎo)航欄顏色重父,我們假設(shè)當(dāng)前控制器為fromVC,返回的控制器為toVC忽匈,如果可以實現(xiàn)從 fromVC 右滑到 toVC 導(dǎo)航欄顏色漸變那么問題就解決了房午!但是導(dǎo)航欄只有一個顏色啊~~~怎么辦?
同樣丹允,可以通過加一層的方法來解決郭厌。我們可以記錄一下 fromVC消失前對應(yīng)的導(dǎo)航欄顏色 和 toVC 當(dāng)前的導(dǎo)航欄顏色,然后根據(jù)右滑進(jìn)度percentComplete雕蔽,來計算漸變色折柠,這樣問題就解決了!
記錄ViewController對應(yīng)導(dǎo)航欄的顏色和透明度
// navigationBar barTintColor
- (UIColor *)wr_navBarBarTintColor
{
UIColor *barTintColor = (UIColor *)objc_getAssociatedObject(self, &kWRNavBarBarTintColorKey);
return (barTintColor != nil) ? barTintColor : [UIColor defaultNavBarBarTintColor];
}
- (void)wr_setNavBarBarTintColor:(UIColor *)color
{
objc_setAssociatedObject(self, &kWRNavBarBarTintColorKey, color, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if ([[self wr_customNavBar] isKindOfClass:[UINavigationBar class]])
{
UINavigationBar *navBar = (UINavigationBar *)[self wr_customNavBar];
[navBar wr_setBackgroundColor:color];
}
else
{
if ([self pushToCurrentVCFinished] == YES && [self pushToNextVCFinished] == NO) {
[self.navigationController setNeedsNavigationBarUpdateForBarTintColor:color];
}
}
}
// navigationBar _UIBarBackground alpha
- (CGFloat)wr_navBarBackgroundAlpha
{
id barBackgroundAlpha = objc_getAssociatedObject(self, &kWRNavBarBackgroundAlphaKey);
return (barBackgroundAlpha != nil) ? [barBackgroundAlpha floatValue] : [UIColor defaultNavBarBackgroundAlpha];
}
- (void)wr_setNavBarBackgroundAlpha:(CGFloat)alpha
{
objc_setAssociatedObject(self, &kWRNavBarBackgroundAlphaKey, @(alpha), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if ([[self wr_customNavBar] isKindOfClass:[UINavigationBar class]])
{
UINavigationBar *navBar = (UINavigationBar *)[self wr_customNavBar];
[navBar wr_setBackgroundAlpha:alpha];
}
else
{
if ([self pushToCurrentVCFinished] == YES && [self pushToNextVCFinished] == NO) {
[self.navigationController setNeedsNavigationBarUpdateForBarBackgroundAlpha:alpha];
}
}
}
交換系統(tǒng)方法 _updateInteractiveTransition(監(jiān)控右滑返回手勢的進(jìn)度)
// swizzling system method: _updateInteractiveTransition
- (void)wr_updateInteractiveTransition:(CGFloat)percentComplete
{
UIViewController *fromVC = [self.topViewController.transitionCoordinator viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [self.topViewController.transitionCoordinator viewControllerForKey:UITransitionContextToViewControllerKey];
[self updateNavigationBarWithFromVC:fromVC toVC:toVC progress:percentComplete];
[self wr_updateInteractiveTransition:percentComplete];
}
根據(jù) fromVC 與 toVC 的導(dǎo)航欄顏色 配合 返回手勢進(jìn)度計算漸變色
+ (UIColor *)middleColor:(UIColor *)fromColor toColor:(UIColor *)toColor percent:(CGFloat)percent
{
CGFloat fromRed = 0;
CGFloat fromGreen = 0;
CGFloat fromBlue = 0;
CGFloat fromAlpha = 0;
[fromColor getRed:&fromRed green:&fromGreen blue:&fromBlue alpha:&fromAlpha];
CGFloat toRed = 0;
CGFloat toGreen = 0;
CGFloat toBlue = 0;
CGFloat toAlpha = 0;
[toColor getRed:&toRed green:&toGreen blue:&toBlue alpha:&toAlpha];
CGFloat newRed = fromRed + (toRed - fromRed) * percent;
CGFloat newGreen = fromGreen + (toGreen - fromGreen) * percent;
CGFloat newBlue = fromBlue + (toBlue - fromBlue) * percent;
CGFloat newAlpha = fromAlpha + (toAlpha - fromAlpha) * percent;
return [UIColor colorWithRed:newRed green:newGreen blue:newBlue alpha:newAlpha];
}
改變導(dǎo)航欄顏色和透明度
- (void)updateNavigationBarWithFromVC:(UIViewController *)fromVC toVC:(UIViewController *)toVC progress:(CGFloat)progress
{
// change navBarBarTintColor
UIColor *fromBarTintColor = [fromVC wr_navBarBarTintColor];
UIColor *toBarTintColor = [toVC wr_navBarBarTintColor];
UIColor *newBarTintColor = [UIColor middleColor:fromBarTintColor toColor:toBarTintColor percent:progress];
[self setNeedsNavigationBarUpdateForBarTintColor:newBarTintColor];
// change navBarTintColor
UIColor *fromTintColor = [fromVC wr_navBarTintColor];
UIColor *toTintColor = [toVC wr_navBarTintColor];
UIColor *newTintColor = [UIColor middleColor:fromTintColor toColor:toTintColor percent:progress];
[self setNeedsNavigationBarUpdateForTintColor:newTintColor];
// change navBarTitleColor
UIColor *fromTitleColor = [fromVC wr_navBarTitleColor];
UIColor *toTitleColor = [toVC wr_navBarTitleColor];
UIColor *newTitleColor = [UIColor middleColor:fromTitleColor toColor:toTitleColor percent:progress];
[self setNeedsNavigationBarUpdateForTitleColor:newTitleColor];
// change navBar _UIBarBackground alpha
CGFloat fromBarBackgroundAlpha = [fromVC wr_navBarBackgroundAlpha];
CGFloat toBarBackgroundAlpha = [toVC wr_navBarBackgroundAlpha];
CGFloat newBarBackgroundAlpha = [UIColor middleAlpha:fromBarBackgroundAlpha toAlpha:toBarBackgroundAlpha percent:progress];
[self setNeedsNavigationBarUpdateForBarBackgroundAlpha:newBarBackgroundAlpha];
}
好了批狐!來看看處理后的效果吧扇售,是不是好多了呢~
4.4 別高興的太早,還有其他問題贾陷。在右滑返回手勢的過程中缘眶,導(dǎo)航欄顏色和透明度會根據(jù)手勢變化而變化。但是一旦松手髓废,系統(tǒng)會自動完成或取消返回操作巷懈。導(dǎo)致透明度停留在最后的那個狀態(tài)。
4.5 咱們來處理右滑返回手勢中斷的問題吧~
通過遵守UINavigationBarDelegate協(xié)議慌洪,實現(xiàn)
optional public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool // same as push methods 方法來監(jiān)聽右滑返回手勢中斷的情況
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
{
__weak typeof (self) weakSelf = self;
id<UIViewControllerTransitionCoordinator> coor = [self.topViewController transitionCoordinator];
if ([coor initiallyInteractive] == YES)
{
NSString *sysVersion = [[UIDevice currentDevice] systemVersion];
if ([sysVersion floatValue] >= 10)
{
[coor notifyWhenInteractionChangesUsingBlock:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
__strong typeof (self) pThis = weakSelf;
[pThis dealInteractionChanges:context];
}];
}
else
{
[coor notifyWhenInteractionEndsUsingBlock:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
__strong typeof (self) pThis = weakSelf;
[pThis dealInteractionChanges:context];
}];
}
return YES;
}
NSUInteger itemCount = self.navigationBar.items.count;
NSUInteger n = self.viewControllers.count >= itemCount ? 2 : 1;
UIViewController *popToVC = self.viewControllers[self.viewControllers.count - n];
[self popToViewController:popToVC animated:YES];
return YES;
}
// deal the gesture of return break off
- (void)dealInteractionChanges:(id<UIViewControllerTransitionCoordinatorContext>)context
{
void (^animations) (UITransitionContextViewControllerKey) = ^(UITransitionContextViewControllerKey key){
UIColor *curColor = [[context viewControllerForKey:key] wr_navBarBarTintColor];
CGFloat curAlpha = [[context viewControllerForKey:key] wr_navBarBackgroundAlpha];
[self setNeedsNavigationBarUpdateForBarTintColor:curColor];
[self setNeedsNavigationBarUpdateForBarBackgroundAlpha:curAlpha];
};
// after that, cancel the gesture of return
if ([context isCancelled] == YES)
{
double cancelDuration = [context transitionDuration] * [context percentComplete];
[UIView animateWithDuration:cancelDuration animations:^{
animations(UITransitionContextFromViewControllerKey);
}];
}
else
{
// after that, finish the gesture of return
double finishDuration = [context transitionDuration] * (1 - [context percentComplete]);
[UIView animateWithDuration:finishDuration animations:^{
animations(UITransitionContextToViewControllerKey);
}];
}
}
處理后:
4.5 同樣顶燕,push和pop也要處理一下 ~
但是push和pop我們拿不到進(jìn)度怎么辦呢?處理辦法是冈爹,通過交換方法涌攻,自己實現(xiàn)給push和pop添加進(jìn)度。
// MARK: swizzling push
static CGFloat wrPushDuration = 0.10;
static int wrPushDisplayCount = 0;
- (CGFloat)wrPushProgress
{
CGFloat all = 60 * wrPushDuration;
int current = MIN(all, wrPushDisplayCount);
return current / all;
}
// swizzling system method: pushViewController
- (void)wr_pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
__block CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(pushNeedDisplay)];
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
[CATransaction setCompletionBlock:^{
[displayLink invalidate];
displayLink = nil;
wrPushDisplayCount = 0;
[viewController setPushToCurrentVCFinished:YES];
}];
[CATransaction setAnimationDuration:wrPushDuration];
[CATransaction begin];
[self wr_pushViewController:viewController animated:animated];
[CATransaction commit];
}
計算push進(jìn)度频伤,并且根據(jù)進(jìn)度更新導(dǎo)航欄顏色和透明度
// change navigationBar barTintColor smooth before push to current VC finished or before pop to current VC finished
- (void)pushNeedDisplay
{
if (self.topViewController != nil && self.topViewController.transitionCoordinator != nil)
{
wrPushDisplayCount += 1;
CGFloat pushProgress = [self wrPushProgress];
UIViewController *fromVC = [self.topViewController.transitionCoordinator viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [self.topViewController.transitionCoordinator viewControllerForKey:UITransitionContextToViewControllerKey];
[self updateNavigationBarWithFromVC:fromVC toVC:toVC progress:pushProgress];
}
}
pop的設(shè)置方法也一樣恳谎,具體請查看代碼 WRNavigationBar_swift
4.6 以上都是改變導(dǎo)航欄的顏色和透明度,同樣改變導(dǎo)航欄的按鈕顏色和標(biāo)題顏色,以及狀態(tài)欄狀態(tài)都和改變顏色一樣因痛,每個ViewController記錄一下婚苹。需要改變的時候,ViewController 改變一下屬性就ok了鸵膏,非常方便膊升!
那么接下來看一下其他demo的動態(tài)效果圖吧~~~
5. 好了,來說說前面提過的自定義導(dǎo)航欄吧
5.1 至于怎么自定義導(dǎo)航欄我就不說了谭企,來說說如果是自定義導(dǎo)航欄廓译,那怎么才能像之前一樣一句代碼改變導(dǎo)航欄屬性。
經(jīng)過封裝债查,自定義導(dǎo)航欄只需要多寫一行代碼!m锿ⅰ院仿!
// 自定義導(dǎo)航欄必須設(shè)置這個屬性!!!!!!!
[self wr_setCustomNavBar:self.navBar];
如果把這行代碼放在基類控制器中,那么其他所有繼承基類控制器都可以一句代碼修改導(dǎo)航欄屬性~~~
看看自定義導(dǎo)航欄的效果吧速和,是不是也很棒
6. 最后看一下移動導(dǎo)航欄的效果
實現(xiàn)代碼
// 設(shè)置導(dǎo)航欄在垂直方向上平移多少距離
- (void)wr_setTranslationY:(CGFloat)translationY
{
// CGAffineTransformMakeTranslation 平移
self.transform = CGAffineTransformMakeTranslation(0, translationY);
}
到這里就結(jié)束??歹垫,具體代碼請前往:
https://github.com/wangrui460/WRNavigationBar
https://github.com/wangrui460/WRNavigationBar_swift
參考資料:
http://www.reibang.com/p/640b64faea9a
http://www.reibang.com/p/7e92451ab0b2
https://github.com/jawadasif/JNAPushPopCompletionBlock
http://www.reibang.com/p/e3ca1b7b6cec
http://www.reibang.com/p/454b06590cf1
歡迎關(guān)注我的微博:wangrui460