现在有很多APP喜欢透明导航栏的UI设计:会在VC页面上方自定义一个头视图,并它的尺寸会随页面向下滑动而动态拉长。
- 喜欢类似UI设计的APP案例,有QQ,哔哩哔哩动画等的一些版本。
- 不多说,直接看效果动图:
如你所见,该项目有以下特点:
- 导航栏透明
- 上部视图的背景色采用渐变色
- 向下滑动时,上部视图的背景图可动态变化
如果你觉得可能有帮助,并且比较急着用,可以点击这里Demo在我的GitHub主页上直接下载,不必看正文介绍。
- 这篇文章,将介绍一下实现的核心模块。
1.实现思路说明
假设需要这个透明效果的是首页VC,在Demo中对应的FirstViewController。
首先,在FirstViewController的m代码文件中,设置导航栏不隐藏,并设置导航栏的背景图为一个透明的ICON资源。这样,可以方便加上左右按钮。
其次,需要在它的XIB文件中做如下图设置,视图有四层:View->ScrollView->groupView->headView+downView。第一个细节的是,ScrollView的顶边距跟父视图要相差-64,即正好为导航栏和状态栏的高度。第二个细节是,groupView的高度要比View高出64。
- 另外,还要给XIB跟代码文件之间建立关系,如下图所示:
2.核心模块说明
FirstViewController.m除了跟XIB拉上的IBOutlet属性,还有两个属性,给顶部视图设置背景图的headerImageView,以及顶部视图的高度参数。
@interface FirstViewController ()<UIScrollViewDelegate>
@property (weak, nonatomic) IBOutlet UIScrollView *scrollView;
@property (weak, nonatomic) IBOutlet UIView *groupView;
@property (weak, nonatomic) IBOutlet UIView *headView;
@property (weak, nonatomic) IBOutlet UIView *downView;
//头部缩放视图图片
@property (nonatomic, strong) UIImageView *headerImageView;
//头部缩放视图图片高度
@property (nonatomic, assign) CGFloat headerScaleImageHeight;
@end
- 其中,给headerImageView设置的懒加载方法如下,这里的关键是给groupView插入headerImageView视图。
- (UIImageView *)headerImageView
{
if (_headerImageView == nil) {
_headerImageView = [[UIImageView alloc] init];
_headerImageView.clipsToBounds = YES;
_headerImageView.contentMode = UIViewContentModeScaleAspectFill;
[self.groupView insertSubview:self.headerImageView atIndex:0];
}
return _headerImageView;
}
- 现在,顶部的背景图也有了,怎么让这个图跟随滑动拉长呢?这就需要在ScrollView的
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
代理方法对该视图的高度进行操作了。具体实现如下:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
// 获取当前偏移量
CGFloat offsetY = scrollView.contentOffset.y;
if (offsetY < 0) {
self.headerImageView.frame = CGRectMake(offsetY, offsetY, self.view.bounds.size.width - offsetY * 2, self.headerScaleImageHeight - offsetY);
} else {
self.headerImageView.frame = CGRectMake(0, 0, self.view.bounds.size.width, self.headerScaleImageHeight);
}
}
- 不可缺失的是,需要在VC的一些生命周期方法里面实现对headView,downView,headerImageView,navigationBar进行一些必要的操作。比如,需要在view加载时对导航栏作如下操作:
//导航栏设置透明
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"TransparentNavBar"] forBarMetrics:UIBarMetricsDefault];
//设置导航栏分割线:透明
if ([self.navigationController.navigationBar respondsToSelector:@selector(shadowImage)]) {
[self.navigationController.navigationBar setShadowImage:[UIImage new]];
}
//显示导航栏
[self.navigationController setNavigationBarHidden:NO animated:YES];
- 还有,设置scrollView代理,设置headView透明等等
//设置scrollView代理
_scrollView.delegate = self;
//顶部视图背景透明
self.headView.backgroundColor = [UIColor clearColor];
不再多说了,直接看完整代码如下
- FirstViewController.m
//
// FirstViewController.m
// TransparentNavigationBar
//
// Created by ChenMan on 2017/6/17.
// Copyright © 2017年 满 陈. All rights reserved.
//
#import "FirstViewController.h"
#import "UIImage+Color.h"
#import "MacroDefinition.h"
@interface FirstViewController ()<UIScrollViewDelegate>
@property (weak, nonatomic) IBOutlet UIScrollView *scrollView;
@property (weak, nonatomic) IBOutlet UIView *groupView;
@property (weak, nonatomic) IBOutlet UIView *headView;
@property (weak, nonatomic) IBOutlet UIView *downView;
//头部缩放视图图片
@property (nonatomic, strong) UIImageView *headerImageView;
//头部缩放视图图片高度
@property (nonatomic, assign) CGFloat headerScaleImageHeight;
@end
@implementation FirstViewController
- (UIImageView *)headerImageView
{
if (_headerImageView == nil) {
_headerImageView = [[UIImageView alloc] init];
_headerImageView.clipsToBounds = YES;
_headerImageView.contentMode = UIViewContentModeScaleAspectFill;
[self.groupView insertSubview:self.headerImageView atIndex:0];
}
return _headerImageView;
}
- (void)viewDidLoad {
[super viewDidLoad];
_scrollView.delegate = self;
//顶部视图背景透明
self.headView.backgroundColor = [UIColor clearColor];
self.downView.layer.masksToBounds = YES;
self.downView.layer.cornerRadius = 6;
UIImage *bgImage = [UIImage bgImageFromColors:@[(id)UIColorFromRGB(0xff7f50), (id)UIColorFromRGB(0xFF8DAC)] withFrame:CGRectMake(0, 0, CGRectGetWidth(_headView.frame), CGRectGetHeight(_headView.frame))];
self.headerScaleImageHeight = 177;
self.headerImageView.frame = CGRectMake(0 , 0, kWidth, self.headerScaleImageHeight);
self.headerImageView.image = bgImage;
// Do any additional setup after loading the view, typically from a nib.
}
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
//导航栏设置透明
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"TransparentNavBar"] forBarMetrics:UIBarMetricsDefault];
//设置导航栏分割线:透明
if ([self.navigationController.navigationBar respondsToSelector:@selector(shadowImage)]) {
[self.navigationController.navigationBar setShadowImage:[UIImage new]];
}
//显示导航栏
[self.navigationController setNavigationBarHidden:NO animated:YES];
}
-(void)viewDidAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
// 设置navigationBar的背景颜色
CGRect frame = CGRectMake(0, 0, kWidth, kStatusBarAndNavigationBarHeight);
NSLog(@"frame: %@", NSStringFromCGRect(frame));
//导航栏设置不透明
[self.navigationController.navigationBar setBackgroundImage:[UIImage bgImageFromColors:@[(id)UIColorFromRGB(0x9ebd3a), (id)UIColorFromRGB(0x0075b6)] withFrame:frame] forBarMetrics:UIBarMetricsDefault];
//设置导航栏分割线
[self.navigationController.navigationBar setTranslucent:YES];
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
// 获取当前偏移量
CGFloat offsetY = scrollView.contentOffset.y;
if (offsetY < 0) {
self.headerImageView.frame = CGRectMake(offsetY, offsetY, self.view.bounds.size.width - offsetY * 2, self.headerScaleImageHeight - offsetY);
} else {
self.headerImageView.frame = CGRectMake(0, 0, self.view.bounds.size.width, self.headerScaleImageHeight);
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
3.小结
- 如你所见,本代码有部分方法依赖其它的分类和宏定义,你可以下载Demo找到它们。
- 点击这里Demo在GitHub上直接下载。
如果可以的话,可以标一个星哈。