CALayer简介
- CALayer一般作为UIView的容器而使用
- CALayer是一个管理着图片载体(image-based content)的层结构, UIView中CALayer的值设定(如frame, backgroundColor)可以覆盖其UIView中的设定
- 直接修改单独创建出的CALayer的属性可以触发隐式动画
- UIView中的CALayer动画必须显式触发才能生效
用CALayer定制下载进度条控件
- 单独创建出CALayer
- 直接修改CALayer的frame值执行隐式动画,实现进度条效果
- 用定时器(NSTimer)模拟网络下载时提供的百分比数据
- 将CALayer封装进UIView子类中定制进度条控件
隐式动画进度条
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) NSTimer *timer; // 定时器
@property (nonatomic, strong) CALayer *layer; // layer
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 设置背景色
self.view.backgroundColor = [UIColor blackColor];
// CALayer
_layer = [CALayer layer];
_layer.frame = CGRectMake(50, 50, 200, 2);
_layer.backgroundColor = [UIColor redColor].CGColor;
[self.view.layer addSublayer:_layer];
// 定时器
_timer = [NSTimer scheduledTimerWithTimeInterval:1.f
target:self
selector:@selector(timerEvent)
userInfo:nil
repeats:YES];
}
- (void)timerEvent {
// 执行layer的隐式动画(取随机值模拟下载进度)
_layer.frame = CGRectMake(50, 50, arc4random()%200, 2);
}
@end
封装UIView实现进度条
progressView.h
#import <UIKit/UIKit.h>
@interface ProgressView : UIView
@property (nonatomic, assign) CGFloat progress; // 进度参数(取值范围为 %0 ~ %100)
@property (nonatomic, strong) UIColor *layerColor; // 修改layer的颜色
@end
progressView.m
#import "ProgressView.h"
@interface ProgressView ()
@property (nonatomic, strong) CALayer *progressLayer;
@property (nonatomic, assign) CGFloat currentViewWidth;
@end
@implementation ProgressView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.progressLayer = [CALayer layer];
self.progressLayer.frame = CGRectMake(0, 0, 0, frame.size.height);
self.progressLayer.backgroundColor = [UIColor redColor].CGColor;
[self.layer addSublayer:self.progressLayer];
// 存储当前view的宽度值
self.currentViewWidth = frame.size.width;
}
return self;
}
#pragma mark - 重写setter,getter方法
@synthesize progress = _progress;
- (void)setProgress:(CGFloat)progress {
_progress = progress;
if (progress <= 0) {
self.progressLayer.frame = CGRectMake(0, 0, 0, self.frame.size.height);
} else if (progress <= 1) {
self.progressLayer.frame = CGRectMake(0, 0,
progress * self.currentViewWidth,
self.frame.size.height);
} else {
self.progressLayer.frame = CGRectMake(0, 0, self.currentViewWidth,
self.frame.size.height);
}
}
- (CGFloat)progress {
return _progress;
}
@synthesize layerColor = _layerColor;
- (void)setLayerColor:(UIColor *)layerColor {
_layerColor = layerColor;
self.progressLayer.backgroundColor = layerColor.CGColor;
}
- (UIColor *)layerColor {
return _layerColor;
}
@end
viewController.m
#import "ViewController.h"
#import "ProgressView.h"
@interface ViewController ()
@property (nonatomic, strong) ProgressView *progressView;
@property (nonatomic, strong) NSTimer *timer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = [UIColor blackColor];
self.progressView = [[ProgressView alloc] initWithFrame:CGRectMake(20, 20, 290, 3)];
self.progressView.layerColor = [UIColor yellowColor];
[self.view addSubview:self.progressView];
// 创建定时器,每一秒执行一回
self.timer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:@selector(layerAnimation)
userInfo:nil
repeats:YES];
}
- (void)layerAnimation {
// 随机获取 0% ~ 100% 给layer赋值
self.progressView.progress = arc4random() % 100 / 100.f;
}
@end
用CALayer定制UIImageView淡入淡出切换图片效果
- 操作UIImageView的CALayer修改其bounds值进行显式动画
- 修改UIImageView的CALayer中的contents属性实现切换图片的动画
- 用CAAnimationGroup将bounds动画与contents动画组合起来
- 将上述效果封装进UIView的子类中生成控件
代码
#import "ViewController.h"
#define NO_EXECUTE 0 // 不执行
#define EXECUTE 1 // 执行
@interface ViewController ()
@property (nonatomic, strong) CALayer *imageLayer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// 创建出独立的layer
self.imageLayer = [CALayer layer];
self.imageLayer.frame = CGRectMake(0, 0, 302, 707);
// 给layer的contents属性设置图片
self.imageLayer.contents = (__bridge id)([UIImage imageNamed:@"起始图片"].CGImage);
[self.view.layer addSublayer:self.imageLayer];
// 3s后执行layer动画
[self performSelector:@selector(layerAnimation)
withObject:nil
afterDelay:3.f];
}
- (void)layerAnimation {
if (NO_EXECUTE) {
// 执行隐式动画(你自己无法控制持续时间等等的参数)
self.imageLayer.contents = (__bridge id)([UIImage imageNamed:@"结束图片"].CGImage);
}
if (NO_EXECUTE) {
// 执行显式动画
// 设定基本动画参数
CABasicAnimation *contentsAnimation = [CABasicAnimation animationWithKeyPath:@"contents"];
contentsAnimation.fromValue = self.imageLayer.contents;
contentsAnimation.toValue = (__bridge id)([UIImage imageNamed:@"结束图片"].CGImage);
contentsAnimation.duration = 3.f;
// 设定layer动画结束后的contents值
self.imageLayer.contents = (__bridge id)([UIImage imageNamed:@"结束图片"].CGImage);
// 让layer开始执行动画
[self.imageLayer addAnimation:contentsAnimation forKey:nil];
}
if (EXECUTE) {
// 执行显式动画
// 基于图片的动画
CABasicAnimation *contentsAnimation = [CABasicAnimation animationWithKeyPath:@"contents"];
contentsAnimation.fromValue = self.imageLayer.contents;
contentsAnimation.toValue = (__bridge id)([UIImage imageNamed:@"结束图片"].CGImage);
contentsAnimation.duration = 0.5f;
// 基于bounds的动画
CABasicAnimation *boundsAnimation = [CABasicAnimation animationWithKeyPath:@"bounds"];
boundsAnimation.fromValue = [NSValue valueWithCGRect:self.imageLayer.bounds];
boundsAnimation.toValue = [NSValue valueWithCGRect:CGRectMake(0, 0, 302/2.f, 707/2.f)];
boundsAnimation.duration = 0.5f;
// 将基于图片的动画与基于bounds的动画组合起来
CAAnimationGroup *groupAnimation = [CAAnimationGroup animation];
groupAnimation.animations = @[contentsAnimation, boundsAnimation];
groupAnimation.duration = 0.5f;
// 设定layer动画结束后的contents值
self.imageLayer.contents = (__bridge id)([UIImage imageNamed:@"结束图片"].CGImage);
self.imageLayer.bounds = CGRectMake(0, 0, 302/2.f, 707/2.f);
// 让layer开始执行动画
[self.imageLayer addAnimation:groupAnimation forKey:nil];
}
}
用CALayer实现复杂遮罩效果
- 遮罩原理的分析
- 用png图片作为CALayer中mask属性的遮罩Layer
- 移动该CALayer的mask的frame值实现遮罩动画效果
CALayer实现遮罩淡化滤镜
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) CALayer *imageLayer;
@property (nonatomic, strong) CALayer *maskLayer;
@property (nonatomic, strong) UIImage *contentImage;
@property (nonatomic, strong) UIImage *maskImage;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = [UIColor blackColor];
// 处理图片
self.contentImage = [UIImage imageNamed:@"原始图片"];
self.maskImage = [UIImage imageNamed:@"maskLayerContents"];
// 生成maskLayer
self.maskLayer = [CALayer layer];
self.maskLayer.frame = CGRectMake(0, 0, 427/2.f, 427/2.f);
self.maskLayer.contents = (__bridge id)(self.maskImage.CGImage);
// 生成contentsLayer
self.imageLayer = [CALayer layer];
self.imageLayer.frame = CGRectMake(0, 0, 427/2.f, 427/2.f);
self.imageLayer.contents = (__bridge id)(self.contentImage.CGImage);
// 给contentsLayer设定mask值
self.imageLayer.mask = self.maskLayer;
// 将contentsLayer添加到layer中
[self.view.layer addSublayer:self.imageLayer];
}
CALayer实现遮罩平移
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) CALayer *imageLayer;
@property (nonatomic, strong) CALayer *maskLayer;
@property (nonatomic, strong) UIImage *imageContents;
@property (nonatomic, strong) UIImage *maskContents;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = [UIColor blackColor];
// 获取图片
self.imageContents = [UIImage imageNamed:@"原始图片"];
self.maskContents = [UIImage imageNamed:@"maskLayerContents"];
// 创建出图片layer
self.imageLayer = [CALayer layer];
self.imageLayer.frame = CGRectMake(50, 50, 200, 200);
self.imageLayer.contents = (__bridge id)(self.imageContents.CGImage);
[self.view.layer addSublayer:self.imageLayer];
// 创建出遮罩layer
self.maskLayer = [CALayer layer];
self.maskLayer.frame = self.imageLayer.bounds;
self.maskLayer.contents = (__bridge id)(self.maskContents.CGImage);
self.maskLayer.backgroundColor = [UIColor whiteColor].CGColor;
// 给图片layer提供遮罩的layer
self.imageLayer.mask = self.maskLayer;
// 3秒钟之后做maskLayer动画
[self performSelector:@selector(maskLayerAnimation)
withObject:nil
afterDelay:3.f];
}
- (void)maskLayerAnimation {
self.maskLayer.frame = CGRectMake(50, 50, 200, 200);
}
@end
定时执行某方法
// 定时器
_timer = [NSTimer scheduledTimerWithTimeInterval:1.f
target:self
selector:@selector(timerEvent)
userInfo:nil
repeats:YES];
随机取值
//随机取0-200中的值
arc4random()%200
进度参数
//(取值范围为 %0 ~ %100)
@property (nonatomic, assign) CGFloat progress;
方法执行开关
#define NO_EXECUTE 0 // 不执行
#define EXECUTE 1 // 执行
// 不执行
if (NO_EXECUTE) {
....
}
// 执行
if (EXECUTE) {
.....
}
动画执行与最终设定
此类动画只是一种提交,并没有最终改变layer的设定。如果结束后,会很快返回到原先的设定。所以要在动画执行前设定好layer值
// 设定layer动画结束后的contents值
self.imageLayer.contents = (__bridge id)([UIImage imageNamed:@"结束图片"].CGImage);
self.imageLayer.bounds = CGRectMake(0, 0, 302/2.f, 707/2.f);
// 让layer开始执行动画
[self.imageLayer addAnimation:groupAnimation forKey:nil];