16-核心动画
2016-03-31 本文已影响22人
木喳喳的夏天
画板(图片处理)
- 为了能对照片提供更多的手势操作,需要把从相册中选取的照片放置到UIImageView中,然后对控件添加手势操作即可完成对照片的对应操作
- 创建一个专门用来处理图片的View(ImageHandleView)
隐式动画
- 只有非根层view的layer才有隐式动画,根层view的layer没有隐式动画
时钟动画
- 首先定义宏,每秒钟秒针、每分钟分针、每小时时针和每分钟时针旋转的度数
// 一秒钟秒针转6°
#define perSecondA 6
// 一分钟分针转6°
#define perMinuteA 6
// 一小时时针转30°
#define perHourA 30
// 每分钟时针转多少度
#define perMinuteHourA 0.5
- 时钟界面的搭建
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *clockView;
@property (nonatomic, weak) CALayer *secondLayer;
@property (nonatomic, weak) CALayer *minuteLayer;
@property (nonatomic, weak) CALayer *hourLayer;
@end
- 在viewDidLoad中对界面进行初始化
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// 添加时针,因为先添加的在下面
[self setUpHourLayer];
// 添加分针
[self setUpMinuteLayer];
// 添加秒针
[self setUpSecondLayer];
// 添加定时器
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timeChange) userInfo:nil repeats:YES];
[self timeChange];
}
- 以此初始化各个指针
#pragma mark - 添加秒针
- (void)setUpSecondLayer
{
CALayer *secondL = [CALayer layer];
secondL.backgroundColor = [UIColor redColor].CGColor;
// 设置锚点
secondL.anchorPoint = CGPointMake(0.5, 1);
secondL.position = CGPointMake(kClockW * 0.5, kClockW * 0.5);
secondL.bounds = CGRectMake(0, 0, 1, kClockW * 0.5 - 20);
[_clockView.layer addSublayer:secondL];
_secondLayer = secondL;
}
#pragma mark - 添加分针
- (void)setUpMinuteLayer
{
CALayer *layer = [CALayer layer];
layer.backgroundColor = [UIColor blackColor].CGColor;
// 设置锚点
layer.anchorPoint = CGPointMake(0.5, 1);
layer.position = CGPointMake(kClockW * 0.5, kClockW * 0.5);
layer.bounds = CGRectMake(0, 0, 4, kClockW * 0.5 - 20);
layer.cornerRadius = 4;
[_clockView.layer addSublayer:layer];
_minuteLayer = layer;
}
#pragma mark - 添加时针
- (void)setUpHourLayer
{
CALayer *layer = [CALayer layer];
layer.backgroundColor = [UIColor blackColor].CGColor;
// 设置锚点
layer.anchorPoint = CGPointMake(0.5, 1);
layer.position = CGPointMake(kClockW * 0.5, kClockW * 0.5);
layer.bounds = CGRectMake(0, 0, 4, kClockW * 0.5 - 40);
layer.cornerRadius = 4;
[_clockView.layer addSublayer:layer];
_hourLayer = layer;
}
- 定时器调用的方法
- (void)timeChange
{
// 获取当前的系统的时间
// 获取当前日历对象
NSCalendar *calendar = [NSCalendar currentCalendar];
// 获取日期的组件:年月日小时分秒
// components:需要获取的日期组件
// fromDate:获取哪个日期的组件
// 经验:以后枚举中有移位运算符,通常一般可以使用并运算(|)
NSDateComponents *cmp = [calendar components:NSCalendarUnitSecond | NSCalendarUnitMinute | NSCalendarUnitHour fromDate:[NSDate date]];
// 获取秒
NSInteger second = cmp.second;
// 获取分
NSInteger minute = cmp.minute;
// 获取小时
NSInteger hour = cmp.hour;
// 计算秒针转多少度
CGFloat secondA = second * perSecondA;
// 计算分针转多少度
CGFloat minuteA = minute * perMinuteA;
// 计算时针转多少度
CGFloat hourA = hour * perHourA + minute * perMinuteHourA;
// 旋转秒针
_secondLayer.transform = CATransform3DMakeRotation(angle2radion(secondA), 0, 0, 1);
// 旋转分针
_minuteLayer.transform = CATransform3DMakeRotation(angle2radion(minuteA), 0, 0, 1);
// 旋转小时
_hourLayer.transform = CATransform3DMakeRotation(angle2radion(hourA), 0, 0, 1);
}
核心动画(CABasicAnimation)
- 创建动画-->描述修改哪个属性产生动画-->设置值-->设置动画执行的次数-->取消动画反弹-->将动画添加到layer上
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 创建动画
CABasicAnimation *anim = [CABasicAnimation animation];
// 描述下修改哪个属性产生动画
// anim.keyPath = @"position";
// 只能是layer属性
anim.keyPath = @"transform.scale";
// 设置值
// anim.toValue = [NSValue valueWithCGPoint:CGPointMake(250, 500)];
anim.toValue = @0.5;
// 设置动画执行次数
anim.repeatCount = MAXFLOAT;
// 取消动画反弹
// 设置动画完成的时候不要移除动画
anim.removedOnCompletion = NO;
// 设置动画执行完成要保持最新的效果
anim.fillMode = kCAFillModeForwards;
[_imageV.layer addAnimation:anim forKey:nil];
}
核心动画(CAKeyFrameAnimation)
- 开始触摸时,创建UIBezierPath并设置起点
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// touch
UITouch *touch = [touches anyObject];
// 获取手指的触摸点
CGPoint curP = [touch locationInView:self];
// 创建路径
UIBezierPath *path = [UIBezierPath bezierPath];
_path = path;
// 设置起点
[path moveToPoint:curP];
}
- 触摸移动时,获取触摸点并添加路径
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
// touch
UITouch *touch = [touches anyObject];
// 获取手指的触摸点
CGPoint curP = [touch locationInView:self];
[_path addLineToPoint:curP];
[self setNeedsDisplay];
}
- 结束触摸时,给imageView添加核心动画
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
// 给imageView添加核心动画
// 添加核心动画
CAKeyframeAnimation *anim = [CAKeyframeAnimation animation];
anim.keyPath = @"position";
// anim.values = @[@(angle2Radion(-10)),@(angle2Radion(10)),@(angle2Radion(-10))];
anim.path = _path.CGPath;
anim.duration = 1;
anim.repeatCount = MAXFLOAT;
[[[self.subviews firstObject] layer] addAnimation:anim forKey:nil];
}
核心动画(CATransition)
- CATransition即转场动画
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 转场代码
if (i == 4) {
i = 1;
}
// 加载图片名称
NSString *imageN = [NSString stringWithFormat:@"%d",i];
_imageView.image = [UIImage imageNamed:imageN];
i++;
// 转场动画
CATransition *anim = [CATransition animation];
anim.type = @"pageCurl";
anim.duration = 2;
[_imageView.layer addAnimation:anim forKey:nil];
}
核心动画(CAAnimationGroup)
- CAAnimationGroup即动画组
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 同时缩放,平移,旋转
CAAnimationGroup *group = [CAAnimationGroup animation];
CABasicAnimation *scale = [CABasicAnimation animation];
scale.keyPath = @"transform.scale";
scale.toValue = @0.5;
CABasicAnimation *rotation = [CABasicAnimation animation];
rotation.keyPath = @"transform.rotation";
rotation.toValue = @(arc4random_uniform(M_PI));
CABasicAnimation *position = [CABasicAnimation animation];
position.keyPath = @"position";
position.toValue = [NSValue valueWithCGPoint:CGPointMake(arc4random_uniform(200), arc4random_uniform(200))];
group.animations = @[scale,rotation,position];
[_redView.layer addAnimation:group forKey:nil];
}
UIView和核心动画(Core Animation)的区别
-
通过分别使用UIView动画和核心动画观察,核心动画并不会真实的改变图层的属性值
-
而UIView动画必须通过修改属性的真实值,才会有动画效果
-
如果以后做动画的时候,不需要与用户交互,通常使用核心动画(比如转场效果)
-
注意:核心动画中,取消反弹的代码必须放在图层添加动画之前
anim.removedOnCompletion = NO; anim.fillMode = kCAFillModeForwards; [self.redView.layer addAnimation:anim forKey:nil];
-
核心动画要想监听动画的完成,需要实现代理,但是不需要遵守任何协议
// 动画完成的时候调用
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
NSLog(@"%@", NSStringFromCGPoint(_redView.layer.position));
}
- UIView动画在完成的时候,可以使用block定义动画完成后要执行的代码
[UIView animateWithDuration:0.25 animations:^{
_redView.layer.position = CGPointMake(150, 400);
} completion:^(BOOL finished) {
NSLog(@"%@", NSStringFromCGPoint(_redView.layer.position));
}];
转盘的设计
- 首先是界面的搭建,直接从xib中加载即可,创建一个类方法返回xib所对应的View
+ (instancetype)wheelView
{
return [[NSBundle mainBundle] loadNibNamed:@"WheelView" owner:nil options:nil][0];
}
- 注意:initWithCoder:方法只是在加载xib的时候会调用,但是并不会将xib中的控件和代码进行连线
- 所以需要在awakeFromNib方法中,进行添加和设置按钮的操作等
- (void)awakeFromNib
{
// UIImageView是个比较特殊的View,默认不会与用户进行交互,需要设置userInteractionEnabled为YES
_centerView.userInteractionEnabled = YES;
CGFloat btnW = 68;
CGFloat btnH = 143;
CGFloat wh = self.bounds.size.width;
// 加载大图片
UIImage *bigImage = [UIImage imageNamed:@"LuckyAstrology"];
// 加载大图片
UIImage *selBigImage = [UIImage imageNamed:@"LuckyAstrologyPressed"];
// 获取当前使用的图片像素和点的比例
CGFloat scale = [UIScreen mainScreen].scale;
CGFloat imageW = bigImage.size.width / 12 * scale;
CGFloat imageH = bigImage.size.height * scale;
// CGImageRef image:需要裁减的图片
// rect:裁减区域
// 裁减区域是以像素为基准
// CGImageCreateWithImageInRect(CGImageRef image, CGRect rect)
// 添加按钮
for (int i = 0; i < 12; i++) {
WheelButton *btn = [WheelButton buttonWithType:UIButtonTypeCustom];
// 设置按钮的位置
btn.layer.anchorPoint = CGPointMake(0.5, 1);
btn.bounds = CGRectMake(0, 0, btnW, btnH);
btn.layer.position = CGPointMake(wh * 0.5, wh * 0.5);
// 按钮的旋转角度
CGFloat radion = (30 * i) / 180.0 * M_PI;
btn.transform = CGAffineTransformMakeRotation(radion);
[_centerView addSubview:btn];
// 加载按钮的图片
// 计算裁减区域
CGRect clipR = CGRectMake(i * imageW, 0, imageW, imageH);
// 裁减图片
CGImageRef imgR = CGImageCreateWithImageInRect(bigImage.CGImage, clipR);
UIImage *image = [UIImage imageWithCGImage:imgR];
// 设置按钮的图片
[btn setImage:image forState:UIControlStateNormal];
// 设置选中状态下图片
imgR = CGImageCreateWithImageInRect(selBigImage.CGImage, clipR);
image = [UIImage imageWithCGImage:imgR];
// 设置按钮的图片
[btn setImage:image forState:UIControlStateSelected];
// 设置选中背景图片
[btn setBackgroundImage:[UIImage imageNamed:@"LuckyRototeSelected"] forState:UIControlStateSelected];
// 监听按钮的点击
[btn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside];
// 默认选中第一个
if (i == 0) {
[self btnClick:btn];
}
}
}
- (void)btnClick:(UIButton *)btn
{
_selBtn.selected = NO;
btn.selected = YES;
_selBtn = btn;
}
- 开始旋转的时候,使用CABasicAnimation创建动画,并添加到self.centerView的layer上
#pragma mark - 开始旋转
- (void)start
{
CABasicAnimation *anim = [CABasicAnimation animation];
anim.keyPath = @"transform.rotation";
anim.toValue = @(M_PI * 2);
anim.duration = 2;
anim.repeatCount = MAXFLOAT;
[_centerView.layer addAnimation:anim forKey:nil];
}
- 当需要修改系统控件的属性的时候,需要自定义控件并集成系统的控件,重写其中的方法即可
// 设置UIImageView的尺寸
// contentRect:按钮的尺寸
- (CGRect)imageRectForContentRect:(CGRect)contentRect
{
// 计算UIImageView控件尺寸
CGFloat imageW = 40;
CGFloat imageH = 46;
CGFloat imageX = (contentRect.size.width - imageW) * 0.5;
CGFloat imageY = 20;
return CGRectMake(imageX, imageY, imageW, imageH);
}
// 取消高亮状态
- (void)setHighlighted:(BOOL)highlighted
{
}