上一篇文章介紹了maskView的原理和基本使用
接下來介紹下maskView的進(jìn)階使用
一.利用maskView使用多種圖片做漸變切換動(dòng)畫
1.素材




// 上述素材按照順序分別是‘base’,'background',''mask','mask1';
self.view.backgroundColor = [UIColor whiteColor];
UIImageView *baseGround = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
baseGround.image = [UIImage imageNamed:@"base"];
baseGround.center = self.view.center;
[self.view addSubview: baseGround];
UIImageView *upGround = [[UIImageView alloc] initWithFrame:baseGround.frame];
upGround.image = [UIImage imageNamed:@"background"];
[self.view addSubview:upGround];
UIView *mask = [[UIView alloc] initWithFrame:upGround.bounds];
upGround.maskView = mask;
UIImageView *picOne = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 400)];
picOne.image = [UIImage imageNamed:@"mask1"];
[mask addSubview:picOne];
UIImageView *picTwo = [[UIImageView alloc] initWithFrame:CGRectMake(100, -200, 100, 400)];
picTwo.image = [UIImage imageNamed:@"mask"];
[mask addSubview:picTwo];
[UIView animateWithDuration:5.f delay:5.f options:0 animations:^{
picOne.y -= 400;
picTwo.y += 400;
} completion:^(BOOL finished) {
}];
- Code原理解析:
mask默認(rèn)色為透明(clearColor),如果picOne和picTwo不存在時(shí),由于mask是透明,切mask又是upGround的maskView,則upGround也會透明,即效果圖只會看到baseGround。在此例中一開始mask有picOne和picTwo兩個(gè)子View,且picOne和picTwo顯示在mask上的部分不是透明部分,所以一開始效果圖只會看到backGround.
- 圖片漸變動(dòng)畫實(shí)現(xiàn)原理:
隨著picOne和picTwo的上下部分移動(dòng),最終的效果是picOne和picTwo顯示在mask上的都是透明部分,當(dāng)顯示在mask上的控件透明時(shí),則mask也會透明,那么backGround也會透明,最終的效果圖就是逐漸看不到backGround,然后顯示baseGround.
運(yùn)行效果圖

二.maskView配合CAGradientLayer使用
iPhone滑動(dòng)解鎖效果
先來個(gè)Demo來解釋下CAGradientLayer吧,先了解下CAGradientLayer是個(gè)啥玩意,
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor grayColor];
// 漸變Layer
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
[self.view.layer addSublayer:gradientLayer];
gradientLayer.frame = CGRectMake(0, 200, self.view.width, 64);
gradientLayer.colors = @[
(__bridge id)[UIColor blackColor].CGColor,
(__bridge id)[UIColor whiteColor].CGColor,
(__bridge id)[UIColor blackColor].CGColor, ];
gradientLayer.locations = @[@0.25,@0.5,@0.75];
}
運(yùn)行效果圖:

CAGradientLayer可用來處理顏色漸變,它的漸變色也可以做隱式動(dòng)畫
此例用frame設(shè)置了layer的位置,也可以用position屬性設(shè)置layer的位置
colors 屬性是設(shè)置CAGradientLayer的漸變顏色(此Demo是從黑-->白-->黑)。
locations 屬性是相對于 colors 設(shè)置的,此Demo中l(wèi)ocations值的含義是 按顏色漸變方向劃分 ,從(0 -> 0.25)部分是黑色,從(0.25 -> 0.5)部分是黑到白的漸變過程,以此類推0.75的含義。
注意
1.CAGradientLayer 默認(rèn)的漸變方向是從上到下,即垂直方向。
2.colors 是個(gè)NSArray類型,只能存對象,所以需要將CGColor轉(zhuǎn)換一下,此屬性可設(shè)置多個(gè)值(1個(gè),2個(gè),3個(gè)等...,都行),此Demo的滑動(dòng)解鎖效果只需要 兩種 顏色的漸變,所以設(shè)置了三個(gè)值,實(shí)際值可根據(jù)需求設(shè)置。
3.設(shè)置好 colors 要設(shè)置好與之相對應(yīng)的 locations 值
4.如果要改變 CAGradientLayer 的漸變方向,則要顯式的給 startPoint和 endPoint 兩個(gè)屬性賦值。
如果要改為水平方向,則需要改成
gradientLayer.startPoint = CGPointMake(0, 0.5);
gradientLayer.endPoint = CGPointMake(1, 0.5)
修改之后的效果圖:

startPoint,endPoint原理解析
既然 CAGradientLayer 可以繪制出漸變顏色的效果,那自然有顏色漸變的方向,所以這兩個(gè)屬性的作用就是設(shè)置顏色漸變的起始點(diǎn)和結(jié)束點(diǎn),這兩個(gè)屬性共同決定了顏色漸變的方向:

接下來來個(gè)CAGradientLayer的動(dòng)效Demo(只需要在上面的代碼添加幾句即可)
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor grayColor];
// 漸變Layer
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
[self.view.layer addSublayer:gradientLayer];
gradientLayer.frame = CGRectMake(0, 200, self.view.width, 64);
gradientLayer.colors = @[
(__bridge id)[UIColor blackColor].CGColor,
(__bridge id)[UIColor whiteColor].CGColor,
(__bridge id)[UIColor blackColor].CGColor, ];
gradientLayer.locations = @[@0.25,@0.5,@0.75];
// 添加部分
CABasicAnimation *basicAnimation = [CABasicAnimation animationWithKeyPath:@"locations"];
basicAnimation.fromValue = @[@0, @0, @0.25];
basicAnimation.toValue = @[@0.75, @1, @1];
basicAnimation.duration = 2.5;
basicAnimation.repeatCount = HUGE;
[gradientLayer addAnimation:basicAnimation forKey:nil];
}
解析:
- 將keyPath賦值為“l(fā)ocations”是讓CAGradientLayer的locations屬性做動(dòng)畫,因?yàn)閘ocations對應(yīng)著顏色,那么顏色也會跟著動(dòng),最終的顯示效果就是:

疑問:但是如何將顏色漸變的動(dòng)畫作用在UILabel的文字上呢 ?
1.其實(shí)非常簡單,就是讓UILabel上的文字稱為CAGradientLayer的遮罩即可。
再來修改下代碼
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor grayColor];
// 漸變Layer
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
[self.view.layer addSublayer:gradientLayer];
gradientLayer.frame = CGRectMake(0, 200, self.view.width, 64);
gradientLayer.colors = @[
(__bridge id)[UIColor blackColor].CGColor,
(__bridge id)[UIColor whiteColor].CGColor,
(__bridge id)[UIColor blackColor].CGColor, ];
gradientLayer.locations = @[@0.25,@0.5,@0.75];
// 添加部分
CABasicAnimation *basicAnimation = [CABasicAnimation animationWithKeyPath:@"locations"];
basicAnimation.fromValue = @[@0, @0, @0.25];
basicAnimation.toValue = @[@0.75, @1, @1];
basicAnimation.duration = 2.5;
basicAnimation.repeatCount = HUGE;
[gradientLayer addAnimation:basicAnimation forKey:nil];
// 第二次添加部分
UILabel *unlock = [[UILabel alloc] initWithFrame:gradientLayer.bounds];
// 必需要強(qiáng)引用保存unlock,此句也可以用[self.view addSubview:unlock]來替代;
self.unlock = unlock;
unlock.alpha = 0.5;
unlock.text = @"滑動(dòng)來解鎖 >>";
unlock.textAlignment = NSTextAlignmentCenter;
unlock.font = [UIFont boldSystemFontOfSize:30];
gradientLayer.mask = unlock.layer;
}
代碼解析:
1.self.unlock = unlock; 用強(qiáng)引用保存label (@property (nonatomic, strong)UILabel *unlock;) ,防止 label dealloc,如果label被銷毀,則label沒有了delegate。
為什么要強(qiáng)引用 unlock 標(biāo)簽 ?
當(dāng)一個(gè)控件(UIiew,UIlabel,UIbutton)創(chuàng)建時(shí),系統(tǒng)會自動(dòng)創(chuàng)建一個(gè)與之相對應(yīng)的layer,layer怎么顯示,實(shí)際是與之對應(yīng)的控件相關(guān)的,layer與之對應(yīng)的控件是delegate關(guān)系,即layer.delegate=當(dāng)前控件,在系統(tǒng)創(chuàng)建layer之后,layer的delegate會執(zhí)行 - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context 方法繪制圖層來顯示給用戶看,當(dāng)控件銷毀了,則不會執(zhí)行此方法了,那么layer上什么也沒有,又因?yàn)榭丶?chuàng)建的默認(rèn)色為clearColor(如果設(shè)置了backgroundColor為不透明,則layer也會不透明),那么layer也會全透明(UIImageView比較特殊,除外)。在此例中如果不強(qiáng)引用保存unlock,執(zhí)行完viewDidload方法后unlock就會銷毀,如果unlock銷毀了,那么unlock相對的layer就是全透明,那么gradientLayer也會全透明,即不強(qiáng)引用unlock的最終顯示效果是 屏幕上什么都看不見。
最終滑動(dòng)解鎖效果圖:

關(guān)于CAGradientLayer的屬性詳細(xì)解析可參考CAGradientLayer的一些屬性解析
三.maskView配合CAShapeLayer使用
這個(gè)Demo沒啥可講的,自己看下源碼,先上效果圖,再上代碼。

#import "UIImage+DDF.h"
#define DEGREES(degrees) ((M_PI * (degrees))/ 180.f)
@interface ViewControllerSix ()
@property (nonatomic, strong) UIView *showView;
@property (nonatomic, strong) CAShapeLayer *maskLayer;
@end
@implementation ViewControllerSix
- (void)handlePan:(UIPanGestureRecognizer *)recognizer
{
// 拖拽
CGPoint translation = [recognizer translationInView:self.view];
recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x,
recognizer.view.center.y + translation.y);
[recognizer setTranslation:CGPointMake(0, 0) inView:self.view];
// 關(guān)閉CoreAnimation的隱式動(dòng)畫,如果開啟隱式動(dòng)畫,會出現(xiàn)卡頓現(xiàn)象。
[CATransaction setDisableActions:YES];
_maskLayer.position = recognizer.view.center;
}
- (void)viewDidLoad
{
[super viewDidLoad];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
imageView.image = [UIImage imageNamed:@"Slice"];
[self.view addSubview:imageView];
_maskLayer = [CAShapeLayer layer];
// 利用貝塞爾曲線創(chuàng)建一個(gè)圓
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(0, 0)
radius:100
startAngle:DEGREES(0)
endAngle:DEGREES(360)
clockwise:YES];
// 獲取path
_maskLayer.path = path.CGPath;
_maskLayer.position = CGPointMake(_showView.bounds.size.width/2.f,
_showView.bounds.size.height/2.f);
_maskLayer.fillColor = [UIColor whiteColor].CGColor;
_maskLayer.position = self.view.center;
UIView *blurView = [[UIView alloc] initWithFrame:self.view.bounds];
blurView.backgroundColor = [UIColor blackColor];
[self.view addSubview:blurView];
// 只顯示圓形部分
blurView.layer.mask = _maskLayer;
blurView.layer.contents = (__bridge id)([[UIImage imageNamed:@"Slice"] imgWithBlur].CGImage);
/*
透明的View,用于maskView中的ShapeLayer的參考View(用于拖拽)
*/
_showView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
_showView.backgroundColor = [UIColor clearColor];
_showView.center = self.view.center;
[self.view addSubview:_showView];
UIPanGestureRecognizer *recognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
[_showView addGestureRecognizer:recognizer];
}
@end
上述所有Demo源碼地址MaskViewDemo