现在的很多app都有这种效果,看起来比较美观。以前都是拿来直接用,今天有时间整理了一下。废话不多说,先看一下效果。
大体思路分为三个步骤:
1、创建UINavigationBar的类别,重写setBackGroundColor方法,控制导航栏颜色
2、创建UIImageView子类,添加毛玻璃层并通过contentOffset改变alpha
3、在ScrollView开始滑动时计算图片缩放比例以及改变导航栏颜色
话不多说,直接上代码~~~~~
1、.m文件设置navigationBar
self.navigationController.navigationBar.barStyle = UIBarStyleDefault;
//去除导航栏底部细线
[self.navigationController.navigationBar setShadowImage:[UIImage new]];
//重写setBackGroundColor方法
[self.navigationController.navigationBar overRideSetBackGroundColor:[UIColor clearColor]];
创建UINavigationBar+Alpha
//runtime 为分类动态添加属性
- (void)setAlphaView:(UIView *)alphaView{
objc_setAssociatedObject(self, &alphaV, alphaView, OBJC_ASSOCIATION_RETAIN);
}
- (UIView *)alphaView{
return objc_getAssociatedObject(self, &alphaV);
}
创建分类时,无法创建self的成员变量,需要用runtime动态为分类添加属性,相关连接请点击“runtime运行时,动态添加成员属性”
- (void)overRideSetBackGroundColor:(UIColor *)backGroundColor{
if (!self.alphaView) {
[self setBackgroundImage:[[UIImage alloc]init] forBarMetrics:UIBarMetricsDefault];
self.alphaView = [[UIView alloc]initWithFrame:CGRectMake(0, -20, Screen_Width, self.bounds.size.height+20)];
self.alphaView.userInteractionEnabled = NO;
self.alphaView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self insertSubview:self.alphaView atIndex:0];
}
self.alphaView.backgroundColor = backGroundColor;
}
给NavigationBar添加一个遮罩层,通过改变alphaView的颜色来控制bar的颜色。alphaView为什么要多出来20?顶部状态栏的有20高度。如果navgationBar直接使用setBackGroundColor会有导航栏默认的颜色,还有一种方法可以设置navigationBar的颜色,是barTintColor,这种方法虽然可以设置导航栏颜色,但是不能设置成clearColor。可以自己试一下看看效果。到这里第一步就完成了,来看看效果。
2、自定义ImageView,思路是在自身ImageView上添加一层毛玻璃ImageVIew,通过kvo监听滚动视图的contentOffset动态改变毛玻璃层的透明度,接下来先上代码~~
BlurView.h文件
#import/**
模糊拉伸view
*/
@interface BlurView : UIImageView
@property (nonatomic, strong) UIImage *originalImage;
@property (nonatomic, assign) CGFloat initialBlurLevel; //初始模糊级别
@property (nonatomic, strong) UIScrollView *scrollView;
@end
初始化毛玻璃层设置透明度为0,初始模糊值0.8
@interface BlurView()
@property (nonatomic, strong) UIImageView *backImageView; //毛玻璃层
@end
@implementation BlurView
- (instancetype)initWithFrame:(CGRect)frame{
if (self = [super initWithFrame:frame]) {
self.initialBlurLevel = 0.8;
self.backImageView = [[UIImageView alloc]initWithFrame:self.bounds];
self.backImageView.contentMode = UIViewContentModeScaleToFill;
self.backImageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.backImageView.backgroundColor = [UIColor clearColor];
self.backImageView.alpha = 0;
[self addSubview:self.backImageView];
}
return self;
}
set方法~~
- (void)setInitialBlurLevel:(CGFloat)initialBlurLevel{
_initialBlurLevel = initialBlurLevel;
}
- (void)setOriginalImage:(UIImage *)originalImage{
_originalImage = originalImage;
self.image = originalImage;
dispatch_queue_t queue = dispatch_queue_create("blur_queue", NULL);
dispatch_async(queue, ^{
UIImage *blurImage = [self applyBlurOnImage:originalImage withRadius:self.initialBlurLevel];
dispatch_async(dispatch_get_main_queue(), ^{
self.backImageView.image = blurImage;
self.backImageView.alpha = 0;
});
});
}
为自身赋值image,并在并发异步线程制作毛玻璃图片,在主线程刷新界面。[self applyBlurOnImage:originalImage withRadius:self.initialBlurLevel];这个是制作毛玻璃图片的方法,网上有多种方法可以实现,我用的是这个▽▽▽▽
- (UIImage *)applyBlurOnImage: (UIImage *)imageToBlur
withRadius:(CGFloat)blurRadius {
if ((blurRadius <= 0.0f) || (blurRadius > 1.0f)) {
blurRadius = 0.5f;
}
int boxSize = (int)(blurRadius * 100);
boxSize -= (boxSize % 2) + 1;
CGImageRef rawImage = imageToBlur.CGImage;
vImage_Buffer inBuffer, outBuffer;
vImage_Error error;
void *pixelBuffer;
CGDataProviderRef inProvider = CGImageGetDataProvider(rawImage);
CFDataRef inBitmapData = CGDataProviderCopyData(inProvider);
inBuffer.width = CGImageGetWidth(rawImage);
inBuffer.height = CGImageGetHeight(rawImage);
inBuffer.rowBytes = CGImageGetBytesPerRow(rawImage);
inBuffer.data = (void*)CFDataGetBytePtr(inBitmapData);
pixelBuffer = malloc(CGImageGetBytesPerRow(rawImage) * CGImageGetHeight(rawImage));
outBuffer.data = pixelBuffer;
outBuffer.width = CGImageGetWidth(rawImage);
outBuffer.height = CGImageGetHeight(rawImage);
outBuffer.rowBytes = CGImageGetBytesPerRow(rawImage);
error = vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer, NULL,
0, 0, boxSize, boxSize, NULL,
kvImageEdgeExtend);
if (error) {
NSLog(@"error from convolution %ld", error);
}
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef ctx = CGBitmapContextCreate(outBuffer.data,
outBuffer.width,
outBuffer.height,
8,
outBuffer.rowBytes,
colorSpace,
CGImageGetBitmapInfo(imageToBlur.CGImage));
CGImageRef imageRef = CGBitmapContextCreateImage (ctx);
UIImage *returnImage = [UIImage imageWithCGImage:imageRef];
//clean up
CGContextRelease(ctx);
CGColorSpaceRelease(colorSpace);
free(pixelBuffer);
CFRelease(inBitmapData);
CGImageRelease(imageRef);
return returnImage;
}
scrollView滚动视图set方法添加kvo键值监听方法
- (void)setScrollView:(UIScrollView *)scrollView{
[_scrollView removeObserver:self forKeyPath:@"contentOffset"];
_scrollView = scrollView;
//kvo添加监听
[_scrollView addObserver:self forKeyPath:@"contentOffset" options:0 context:nil];
}
监听方法的实现
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void *)context{
CGFloat blur = -self.scrollView.contentOffset.y/self.bounds.size.height;
NSLog(@"blur ==== %.2f",blur);
self.backImageView.alpha = blur*4;
}
写到这里下拉时已经可以看到效果了,可以自己调整blur值进行调试。调试的时候会发现控制器的顶部依旧会有一个透明的导航栏,我刚看到时也是一脸懵b。后来发现只是少写了一句代码,就是这货▽▽▽▽
//很重要的一句话 简单点说就是automaticallyAdjustsScrollViewInsets根据按所在界面的status bar,navigationbar,与tabbar的高度,自动调整scrollview的 inset,设置为no,不让viewController调整,我们自己修改布局即可~
self.automaticallyAdjustsScrollViewInsets = NO;
在控制器里加上这句就ok了,现在的效果是这样~~
3、接下来最后一步,通过“scrollViewDidScroll:(UIScrollView *)scrollView”改变view的frame以及导航栏的透明度
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
CGFloat offsetY = scrollView.contentOffset.y;
if (offsetY < 0) {
//拉伸后图片总高度
CGFloat totalHeight = topViewHeight - offsetY;
//图片放大比例
CGFloat scale = totalHeight / topViewHeight;
self.blurView.frame = CGRectMake(-(Screen_Width*scale - Screen_Width)/2, offsetY, Screen_Width * scale, totalHeight);
}
UIColor *color = RGB(6, 173, 114);
if (offsetY > 0) {
CGFloat alpha = offsetY/64;
[self.navigationController.navigationBar overRideSetBackGroundColor:[color colorWithAlphaComponent:alpha]];
self.navigationController.navigationBar.barStyle = UIBarStyleDefault;
}else{
[self.navigationController.navigationBar overRideSetBackGroundColor:[color colorWithAlphaComponent:0]];
}
}
这段代码分offset.y<0和>0,<0是用来计算view的缩放比例,重设frame,>0改变导航栏颜色。现在效果已经出来了▽▽▽▽
基本的需求已经出来了,有想让效果看起来更自然一些的小伙伴可以试着在玻璃层上再添加一个view层,思路与玻璃层是一样的,只是通过view的颜色来调整界面。花了些时间整理出来,分享给大家,也供自己日后复习,小弟菜鸟一枚,还请笑纳~~~~!!!