最近项目要用到透明导航栏,如果只是单纯的将导航栏设置为隐藏,则在切换页面的时候过度就很生硬,体验很不好,网上搜索了好几个实例看了下,基本都是用runtime的黑魔法实现的,但是效果都没有达到我需要的效果,所以就综合几个示例,完成了下面的demo,切换效果还是很好,很平滑,达到了预期的效果。
本示例也是通过runtime黑魔法来实现的,这里做个记录,有需要的可以拿过去用,本篇会贴出全部的关键代码段
效果图
工程文件结构如下图
//
// UINavigationController+SMA.h
// navDemo
//
#import <UIKit/UIKit.h>
@interface UINavigationController (SMAUI) <UINavigationBarDelegate,UINavigationControllerDelegate>
/**
设置导航栏背景透明度
@param alpha 透明度的浮点值
*/
-(void)setNavigationBackground:(CGFloat)alpha;
@end
//
// UINavigationController+SMA.m
// navDemo
//
#import "UINavigationController+SMA.h"
#import "UIViewController+SMA.h"
#import <objc/runtime.h>
@implementation UINavigationController (SMAUI)
//设置导航栏背景透明度
-(void)setNavigationBackground:(CGFloat)alpha
{
// 导航栏背景透明度设置
UIView *barBackgroundView = [[self.navigationBar subviews] objectAtIndex:0];// _UIBarBackground
UIImageView *backgroundImageView = [[barBackgroundView subviews] objectAtIndex:0];// UIImageView
//去掉导航栏下的黑线
UIView *shadowView = (UIView *)[barBackgroundView valueForKey:@"_shadowView"];
if (shadowView) {
shadowView.alpha = alpha;
}
if (self.navigationBar.isTranslucent) {
if (backgroundImageView != nil && backgroundImageView.image != nil) {
barBackgroundView.alpha = alpha;
} else {
UIView *backgroundEffectView = [[barBackgroundView subviews] objectAtIndex:1];// UIVisualEffectView
if (backgroundEffectView != nil) {
backgroundEffectView.alpha = alpha;
}
}
} else {
barBackgroundView.alpha = alpha;
}
}
+ (void)initialize {
if (self == [UINavigationController self]) {
// 交换方法
SEL originalSelector = NSSelectorFromString(@"_updateInteractiveTransition:");
SEL swizzledSelector = NSSelectorFromString(@"et__updateInteractiveTransition:");
Method originalMethod = class_getInstanceMethod([self class], originalSelector);
Method swizzledMethod = class_getInstanceMethod([self class], swizzledSelector);
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
// 交换的方法,监控滑动手势
- (void)et__updateInteractiveTransition:(CGFloat)percentComplete {
[self et__updateInteractiveTransition:(percentComplete)];
UIViewController *topVC = self.topViewController;
if (topVC != nil) {
id<UIViewControllerTransitionCoordinator> coor = topVC.transitionCoordinator;
if (coor != nil) {
// 随着滑动的过程设置导航栏透明度渐变
CGFloat fromAlpha = [[coor viewControllerForKey:UITransitionContextFromViewControllerKey].navBarBgAlpha floatValue];
CGFloat toAlpha = [[coor viewControllerForKey:UITransitionContextToViewControllerKey].navBarBgAlpha floatValue];
CGFloat nowAlpha = fromAlpha + (toAlpha - fromAlpha) * percentComplete;
// NSLog(@"from:%f, to:%f, now:%f",fromAlpha, toAlpha, nowAlpha);
[self setNavigationBackground:nowAlpha];
}
}
}
#pragma mark - UINavigationController Delegate
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
UIViewController *topVC = self.topViewController;
if (topVC != nil) {
id<UIViewControllerTransitionCoordinator> coor = topVC.transitionCoordinator;
if (coor != nil) {
[coor notifyWhenInteractionChangesUsingBlock:^(id<UIViewControllerTransitionCoordinatorContext> context){
[self dealInteractionChanges:context];
}];
}
}
}
- (void)dealInteractionChanges:(id<UIViewControllerTransitionCoordinatorContext>)context {
if ([context isCancelled]) {// 自动取消了返回手势
NSTimeInterval cancelDuration = [context transitionDuration] * (double)[context percentComplete];
[UIView animateWithDuration:cancelDuration animations:^{
CGFloat nowAlpha = [[context viewControllerForKey:UITransitionContextFromViewControllerKey].navBarBgAlpha floatValue];
NSLog(@"自动取消返回到alpha:%f", nowAlpha);
[self setNavigationBackground:nowAlpha];
}];
} else {// 自动完成了返回手势
NSTimeInterval finishDuration = [context transitionDuration] * (double)(1 - [context percentComplete]);
[UIView animateWithDuration:finishDuration animations:^{
CGFloat nowAlpha = [[context viewControllerForKey:
UITransitionContextToViewControllerKey].navBarBgAlpha floatValue];
NSLog(@"自动完成返回到alpha:%f", nowAlpha);
[self setNavigationBackground:nowAlpha];
}];
}
}
#pragma mark - UINavigationBar Delegate
- (void)navigationBar:(UINavigationBar *)navigationBar didPopItem:(UINavigationItem *)item {
if (self.viewControllers.count >= navigationBar.items.count) {// 点击返回按钮
UIViewController *popToVC = self.viewControllers[self.viewControllers.count - 1];
[self setNavigationBackground:[popToVC.navBarBgAlpha floatValue]];
}
}
- (void)navigationBar:(UINavigationBar *)navigationBar didPushItem:(UINavigationItem *)item {
// push到一个新界面
[self setNavigationBackground:[self.topViewController.navBarBgAlpha floatValue]];
}
@end
//
// UIViewController+SMA.h
// navDemo
//
#import <UIKit/UIKit.h>
///为系统的UIViewController动态增加属性
@interface UIViewController (SMAUI)
//vc的导航bar背景透明度
@property (copy,nonatomic) NSString *navBarBgAlpha;
@end
//
// UIViewController+SMA.m
// navDemo
//
#import "UIViewController+SMA.h"
#import "UINavigationController+SMA.h"
#import <objc/runtime.h>
@implementation UIViewController (SMAUI)
//属性对应的key 必须是C语言字符串
static char *SMAUI = "SMAUI";
-(void)setNavBarBgAlpha:(NSString *)navBarBgAlpha{
objc_setAssociatedObject(self, SMAUI, navBarBgAlpha, OBJC_ASSOCIATION_COPY_NONATOMIC);
// 设置导航栏透明度(利用Category自己添加的方法)
[self.navigationController setNavigationBackground:[navBarBgAlpha floatValue]];
}
-(NSString *)navBarBgAlpha{
return objc_getAssociatedObject(self, SMAUI);
}
@end
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
self.window = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];
FirstViewController *vc1 = [[FirstViewController alloc]init];
UINavigationController *nav1 = [[UINavigationController alloc]initWithRootViewController:vc1];
nav1.tabBarItem = [[UITabBarItem alloc] initWithTabBarSystemItem:UITabBarSystemItemFavorites tag:1];
//设置导航栏的背景颜色和title颜色
nav1.navigationBar.tintColor = [UIColor grayColor];
[nav1.navigationBar setTintColor:[UIColor whiteColor]];
[nav1.navigationBar setBarTintColor:[UIColor grayColor]];
[nav1.navigationBar setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[UIColor whiteColor],NSForegroundColorAttributeName,nil]];
nav1.navigationBar.shadowImage = [UIImage new];
SecondViewController *vc2 = [[SecondViewController alloc]init];
UINavigationController *nav2 = [[UINavigationController alloc]initWithRootViewController:vc2];
nav2.tabBarItem = [[UITabBarItem alloc] initWithTabBarSystemItem:UITabBarSystemItemContacts tag:2];
UITabBarController *tab = [[UITabBarController alloc]init];
[tab setViewControllers:@[nav1,nav2]];
self.window.rootViewController = tab;
//设置状态栏字体为白色
[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleDefault;
[self.window makeKeyAndVisible];
return YES;
}
//
// FirstViewController.m
// navDemo
//
#import "FirstViewController.h"
#import "UIViewController+SMA.h"
#import "SecondViewController.h"
#import "ThirdViewController.h"
@interface FirstViewController ()
@end
@implementation FirstViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor lightGrayColor];
self.title = @"First";
UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(100, 100, 200, 50)];
[btn setTitle:@"Next View" forState:UIControlStateNormal];
[btn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[btn addTarget:self action:@selector(toNextView) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn];
}
// 按钮响应
- (void)toNextView {
ThirdViewController *VC3 = [[ThirdViewController alloc] init];
[self.navigationController pushViewController:VC3 animated:YES];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.navigationController.navigationBar.subviews.firstObject.alpha = 1.0;
self.navBarBgAlpha = @"1.0";
}
@end
//
// SecondViewController.m
// navDemo
//
#import "SecondViewController.h"
#import "UIViewController+SMA.h"
#import "ThirdViewController.h"
@interface SecondViewController ()
@end
@implementation SecondViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor lightGrayColor];
UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(100, 100, 200, 50)];
[btn setTitle:@"Next View" forState:UIControlStateNormal];
[btn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[btn addTarget:self action:@selector(toNextView) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn];
}
// 按钮响应
- (void)toNextView {
ThirdViewController *VC3 = [[ThirdViewController alloc] init];
[self.navigationController pushViewController:VC3 animated:YES];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.navBarBgAlpha = @"0.0";
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end
//
// ThirdViewController.m
// navDemo
//
#import "ThirdViewController.h"
#import "UIViewController+SMA.h"
#define sWidth [UIScreen mainScreen].bounds.size.width
#define sHeight [UIScreen mainScreen].bounds.size.height
@interface ThirdViewController ()<UITableViewDelegate,UITableViewDataSource>
@property(nonatomic,strong) UITableView *tableView;
@end
@implementation ThirdViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor lightGrayColor];
[self.view addSubview:self.tableView];
}
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear: YES];
self.navBarBgAlpha = @"0.0";
}
#pragma mark UITableViewDelegate
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat minAlphaOffset = -64;
CGFloat maxAlphaOffset = 200;
CGFloat offset = scrollView.contentOffset.y;
CGFloat alpha = (offset - minAlphaOffset) / (maxAlphaOffset - minAlphaOffset);
self.navBarBgAlpha = [NSString stringWithFormat:@"%f",alpha];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"reuseIdentifier"];
cell.textLabel.text = [NSString stringWithFormat:@"%ld",indexPath.row];
return cell;
}
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
CGFloat viewHeght = 150 * (sWidth / 320);
UIView *topView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, sWidth, viewHeght)];
topView.backgroundColor = [UIColor colorWithRed:6/255.0 green:193/255.0 blue:174/255.0 alpha:1.0];
return topView;
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 30;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 50;
}
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 150 * (sWidth / 320);
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
-(UITableView *)tableView
{
if (!_tableView) {
_tableView = [[UITableView alloc]initWithFrame:CGRectMake(0, -64, sWidth, sHeight + 64) style:UITableViewStyleGrouped];
_tableView.delegate = self;
_tableView.dataSource = self;
}
return _tableView;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end