美文网首页
16-核心动画

16-核心动画

作者: 木喳喳的夏天 | 来源:发表于2016-03-31 16:03 被阅读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
    {
        
    }
    

    相关文章

      网友评论

          本文标题:16-核心动画

          本文链接:https://www.haomeiwen.com/subject/ntwelttx.html