iOS - Core Animation 核心动画的使用

简介: 1、简单使用示例1.1 时钟QClockView.h @interface QClockView : UIView /// 创建时钟界面 + (instancetype)q_clockViewWithFrame:(CGRect)frame; @endQClockView.

1、简单使用示例

1.1 时钟

  • QClockView.h

        @interface QClockView : UIView
    
        /// 创建时钟界面
        + (instancetype)q_clockViewWithFrame:(CGRect)frame;
    
        @end
  • QClockView.m

        #define CLOCK_WIDTH self.bounds.size.width
    
        @interface QClockView ()
    
        // 表盘
        @property (nonatomic, strong) UIImageView *clockView;
    
        // 指针
        @property (nonatomic, strong) CALayer *secondLayer;
        @property (nonatomic, strong) CALayer *minuteLayer;
        @property (nonatomic, strong) CALayer *hourLayer;
    
        @end
    
        @implementation QClockView
    
        /// 创建时钟界面
        + (instancetype)q_clockViewWithFrame:(CGRect)frame {
    
            QClockView *clockView = [[self alloc] initWithFrame:frame];
            return clockView;
        }
    
        /// 初始化
        - (instancetype)initWithFrame:(CGRect)frame {
    
            if (self = [super initWithFrame:frame]) {
    
                // 添加表盘
                self.clockView = [[UIImageView alloc] init];
                self.clockView.image = [UIImage imageNamed:@"clock"];
                [self addSubview:self.clockView];
    
                // 添加定时器
                [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timeChange) userInfo:nil repeats:YES];
    
                [self timeChange];
            }
            return self;
        }
    
        /// 布局子控件
        - (void)layoutSubviews {
            [super layoutSubviews];
    
            self.clockView.frame = self.bounds;
        }
    
        // 定时器响应事件处理
        - (void)timeChange {
    
            // 获取当前的系统的时间
    
            // 获取当前日历对象
            NSCalendar *calendar = [NSCalendar currentCalendar];
    
            // 获取日期的组件:年月日小时分秒
            NSDateComponents  *cmp = [calendar components:NSCalendarUnitSecond |
                                      NSCalendarUnitMinute |
                                      NSCalendarUnitHour
                                                 fromDate:[NSDate date]];
    
            // 获取秒
            NSInteger second = cmp.second + 1;
    
            // 获取分
            NSInteger minute = cmp.minute;
    
            // 获取小时
            NSInteger hour = cmp.hour;
    
            // 计算秒针转多少度,一秒钟秒针转 6°
            CGFloat secondA = second * 6;
    
            // 计算分针转多少度,一分钟分针转 6°
            CGFloat minuteA = minute * 6;
    
            // 计算时针转多少度,一小时时针转 30°,每分钟时针转 0.5°
            CGFloat hourA = hour * 30 + minute * 0.5;
    
            // 旋转秒针,使用 layer 的隐式动画属性产生动画效果
            self.secondLayer.transform = CATransform3DMakeRotation(secondA / 180 * M_PI, 0, 0, 1);
    
            // 旋转分针
            self.minuteLayer.transform = CATransform3DMakeRotation(minuteA / 180 * M_PI, 0, 0, 1);
    
            // 旋转小时
            self.hourLayer.transform = CATransform3DMakeRotation(hourA / 180 * M_PI, 0, 0, 1);
        }
    
        // 添加秒针
        - (CALayer *)secondLayer {
    
            if (_secondLayer == nil) {
    
                _secondLayer = [CALayer layer];
                _secondLayer.backgroundColor = [UIColor redColor].CGColor;
    
                _secondLayer.anchorPoint = CGPointMake(0.5, 1);
                _secondLayer.position = CGPointMake(CLOCK_WIDTH * 0.5, CLOCK_WIDTH * 0.5);
                _secondLayer.bounds = CGRectMake(0, 0, 1, CLOCK_WIDTH * 0.5 - 20);
    
                [self.clockView.layer addSublayer:_secondLayer];
            }
            return _secondLayer;
        }
    
        // 添加分针
        - (CALayer *)minuteLayer {
    
            if (_minuteLayer == nil) {
    
                _minuteLayer = [CALayer layer];
                _minuteLayer.backgroundColor = [UIColor blackColor].CGColor;
    
                _minuteLayer.anchorPoint = CGPointMake(0.5, 1);
                _minuteLayer.position = CGPointMake(CLOCK_WIDTH * 0.5, CLOCK_WIDTH * 0.5);
                _minuteLayer.bounds = CGRectMake(0, 0, 4, CLOCK_WIDTH * 0.5 - 20);
                _minuteLayer.cornerRadius = 2;
    
                [self.clockView.layer addSublayer:_minuteLayer];
            }
            return _minuteLayer;
        }
    
        // 添加时针
        - (CALayer *)hourLayer {
    
            if (_hourLayer == nil) {
    
                _hourLayer = [CALayer layer];
                _hourLayer.backgroundColor = [UIColor blackColor].CGColor;
    
                _hourLayer.anchorPoint = CGPointMake(0.5, 1);
                _hourLayer.position = CGPointMake(CLOCK_WIDTH * 0.5, CLOCK_WIDTH * 0.5);
                _hourLayer.bounds = CGRectMake(0, 0, 4, CLOCK_WIDTH * 0.5 - 40);
                _hourLayer.cornerRadius = 2;
    
                [self.clockView.layer addSublayer:_hourLayer];
            }
            return _hourLayer;
        }
    
        @end
  • ViewController.m

        // 创建时钟界面
        QClockView *clockView = [QClockView q_clockViewWithFrame:CGRectMake(100, 100, 200, 200)];
        [self.view addSubview:clockView];
  • 效果

    animation71

1.2 转盘

  • QWheelButton.h

        @interface QWheelButton : UIButton
    
        @end
  • QWheelButton.m

        @implementation QWheelButton
    
        /// 设置按钮中 imageView 的尺寸
        - (CGRect)imageRectForContentRect:(CGRect)contentRect {
    
            // 计算 imageView 控件尺寸,contentRect 为按钮的尺寸
            CGFloat W = 40;
            CGFloat H = 46;
            CGFloat X = (contentRect.size.width - W) * 0.5;
            CGFloat Y = 20;
    
            return CGRectMake(X, Y, W, H);
        }
    
        /// 设置按钮接收点击事件的区域
        - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    
            CGFloat btnW = self.bounds.size.width;
            CGFloat btnH = self.bounds.size.height;
    
            // 计算不接收点击事件的区域
            CGFloat X = 0;
            CGFloat Y = btnH / 2;
            CGFloat W = btnW;
            CGFloat H = Y;
            CGRect rect = CGRectMake(X, Y, W, H);
    
            if (CGRectContainsPoint(rect, point)) {
                return nil;
            } else {
                return [super hitTest:point withEvent:event];
            }
        }
    
        @end
  • QWheelView.h

        @interface QWheelView : UIView
    
        /// 创建转盘界面
        + (instancetype)q_wheelViewWithFrame:(CGRect)frame;
    
        /// 开始转动
        - (void)q_startAnimating;
    
        /// 停止转动
        - (void)q_stopAnimating;
    
        @end
  • QWheelView.m

        #import "QWheelButton.h"
    
        #define WHEEL_WIDTH     self.bounds.size.width
    
        @interface QWheelView () <CAAnimationDelegate>
    
        /// 转盘控件
        @property (nonatomic, strong) UIImageView *backImageView;
        @property (nonatomic, strong) UIImageView *centerImagerView;
        @property (nonatomic, strong) UIButton *startButton;
    
        /// 当前选中的按钮
        @property (nonatomic, weak) UIButton *selectedButton;
    
        /// 显示定时器
        @property (nonatomic, strong) CADisplayLink *link;
    
        @end
    
        @implementation QWheelView
    
        /// 创建转盘界面
        + (instancetype)q_wheelViewWithFrame:(CGRect)frame {
    
            QWheelView *wheelView = [[self alloc] initWithFrame:frame];
            return wheelView;
        }
    
        /// 开始转动
        - (void)q_startAnimating {
    
            self.link.paused = NO;
        }
    
        /// 停止转动
        - (void)q_stopAnimating {
    
            self.link.paused = YES;
        }
    
        /// 初始化转盘控件
        - (instancetype)initWithFrame:(CGRect)frame {
    
            if (self = [super initWithFrame:frame]) {
    
                // 添加转盘界面
    
                self.backImageView = [[UIImageView alloc] init];
                self.backImageView.image = [UIImage imageNamed:@"LuckyBaseBackground"];
                [self addSubview:self.backImageView];
    
                self.centerImagerView = [[UIImageView alloc] init];
                self.centerImagerView.image = [UIImage imageNamed:@"LuckyRotateWheel"];
                self.centerImagerView.userInteractionEnabled = YES;
                [self addSubview:self.centerImagerView];
    
                // 添加开始选号按钮
    
                self.startButton = [[UIButton alloc] init];
                [self.startButton setBackgroundImage:[UIImage imageNamed:@"LuckyCenterButton"] forState:UIControlStateNormal];
                [self.startButton setBackgroundImage:[UIImage imageNamed:@"LuckyCenterButtonPressed"] forState:UIControlStateHighlighted];
                [self.startButton addTarget:self action:@selector(startPicker:) forControlEvents:UIControlEventTouchUpInside];
                [self addSubview:self.startButton];
    
                // 添加号码按钮
    
                // 加载大图片
                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;
    
                for (int i = 0; i < 12; i++) {
    
                    QWheelButton *button = [QWheelButton buttonWithType:UIButtonTypeCustom];
                    [self.centerImagerView addSubview:button];
    
                    // 设置按钮图片
                    CGRect clipR = CGRectMake(i * imageW, 0, imageW, imageH);
                    [button setImage:[self getImageWithClipRect:clipR fromImage:bigImage] forState:UIControlStateNormal];
                    [button setImage:[self getImageWithClipRect:clipR fromImage:selBigImage] forState:UIControlStateSelected];
                    [button setBackgroundImage:[UIImage imageNamed:@"LuckyRototeSelected"] forState:UIControlStateSelected];
    
                    // 监听按钮的点击
                    [button addTarget:self action:@selector(buttonClick:) forControlEvents:UIControlEventTouchUpInside];
    
                    // 默认选中第一个
                    if (i == 0) {
                        [self buttonClick:button];
    
                        // button.backgroundColor = [UIColor blueColor];
                    }
                }
            }
            return self;
        }
    
        /// 布局子控件
        - (void)layoutSubviews {
            [super layoutSubviews];
    
            // 转盘界面
    
            self.backImageView.frame = CGRectMake(0, 0, WHEEL_WIDTH, WHEEL_WIDTH);
    
            self.centerImagerView.layer.position = CGPointMake(WHEEL_WIDTH * 0.5, WHEEL_WIDTH * 0.5);
            self.centerImagerView.bounds = CGRectMake(0, 0, WHEEL_WIDTH, WHEEL_WIDTH);
    
            // 开始选号按钮
    
            self.startButton.frame = CGRectMake((WHEEL_WIDTH - WHEEL_WIDTH / 3.5) / 2,
                                                (WHEEL_WIDTH - WHEEL_WIDTH / 3.5) / 2,
                                                WHEEL_WIDTH / 3.5,
                                                WHEEL_WIDTH / 3.5);
    
            // 号码按钮
    
            CGFloat btnW = 68;
            CGFloat btnH = 143;
    
            for (int i = 0; i < 12; i++) {
    
                QWheelButton *button = self.centerImagerView.subviews[i];
    
                // 按钮尺寸位置
                button.bounds = CGRectMake(0, 0, btnW, btnH);
                button.layer.position = CGPointMake(WHEEL_WIDTH * 0.5, WHEEL_WIDTH * 0.5);
                button.layer.anchorPoint = CGPointMake(0.5, 1);
    
                // 按钮的旋转角度
                CGFloat radion = (30 * i) / 180.0 * M_PI;
                button.transform = CGAffineTransformMakeRotation(radion);
            }
        }
    
        /// 定时器触发事件处理
        - (void)angleChange {
    
            // 每一次调用旋转多少 90 / 60.0
            CGFloat angle = (90 / 60.0) * M_PI / 180.0;
    
            // 持续旋转,需要设置 centerImagerView 的 layer.position 值
            self.centerImagerView.transform = CGAffineTransformRotate(self.centerImagerView.transform, angle);
        }
    
        /// 按钮选中点击事件处理
        - (void)buttonClick:(UIButton *)button {
    
            // 取消之前的按钮选择状态
            self.selectedButton.selected = NO;
    
            // 设置当前点击的按钮选择状态
            button.selected = YES;
            self.selectedButton = button;
        }
    
        /// 点击开始选号的时候
        - (void)startPicker:(UIButton *)button {
    
            // 不需要定时器旋转
            self.link.paused = YES;
    
            // 中间的转盘快速的旋转,并且不需要与用户交互
            CABasicAnimation *anim = [CABasicAnimation animation];
            anim.keyPath = @"transform.rotation";
            anim.toValue = @(M_PI * 2 * 3);
            anim.duration = 0.5;
            anim.delegate = self;
            [self.centerImagerView.layer addAnimation:anim forKey:nil];
    
            // 根据选中的按钮获取旋转的度数,通过 transform 获取角度
            CGFloat angle = atan2(self.selectedButton.transform.b, self.selectedButton.transform.a);
    
            // 点击哪个星座,就把当前星座指向中心点上面
            self.centerImagerView.transform = CGAffineTransformMakeRotation(-angle);
        }
    
        /// 动画协议方法
        - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
    
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    
                self.link.paused = NO;
            });
        }
    
        /// 懒加载定时器
        - (CADisplayLink *)link {
    
            if (_link == nil) {
    
                _link = [CADisplayLink displayLinkWithTarget:self selector:@selector(angleChange)];
                [_link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
            }
            return _link;
        }
    
        /// 裁剪图片
        - (UIImage *)getImageWithClipRect:(CGRect)clipR fromImage:(UIImage *)image {
    
            // 裁剪图片,裁剪区域是以像素为基准
            CGImageRef cgImage = CGImageCreateWithImageInRect(image.CGImage, clipR);
    
            return [UIImage imageWithCGImage:cgImage];
        }
    
        @end
  • ViewController.m

        // 创建转盘界面
        QWheelView *wheelView = [QWheelView q_wheelViewWithFrame:CGRectMake(40, 150, 300, 300)];
        [self.view addSubview:wheelView];
        self.wheelView = wheelView;
    
        // 开始转动
        [self.wheelView q_startAnimating];
    
        // 停止转动
        [self.wheelView q_stopAnimating];
  • 效果

    animation72

1.3 折叠图片

  • 一张图片必须要通过两个控件展示,旋转的时候,只旋转一部分控件,通过 layer 控制图片的显示内容,可以让一张完整的图片通过两个控件显示。

        @interface ViewController ()
    
        // 上下两部分图片绘制控件
        @property (nonatomic, strong) UIImageView *topImageView;
        @property (nonatomic, strong) UIImageView *bottomImageView;
    
        // 触控控件
        @property (nonatomic, strong) UIView *dragView;
    
        // 渐变图层
        @property (nonatomic, strong) CAGradientLayer *gradientL;
    
        @end
    
        @implementation ViewController
    
        - (void)viewDidLoad {
            [super viewDidLoad];
    
            self.view.backgroundColor = [UIColor cyanColor];
    
            // 添加上下两部分图片绘制控件,两个控件叠加在一起,且在位于 drawView 中间
    
            self.bottomImageView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 250, 200, 100)];
            self.bottomImageView.image = [UIImage imageNamed:@"demo"];
            [self.view addSubview:self.bottomImageView];
    
            self.topImageView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 250, 200, 100)];
            self.topImageView.image = [UIImage imageNamed:@"demo"];
            [self.view addSubview:self.topImageView];
    
            // 设置图片显示的尺寸
    
            self.topImageView.layer.contentsRect = CGRectMake(0, 0, 1, 0.5);            // 范围 0 ~ 1
            self.topImageView.layer.anchorPoint = CGPointMake(0.5, 1);
    
            self.bottomImageView.layer.contentsRect = CGRectMake(0, 0.5, 1, 0.5);
            self.bottomImageView.layer.anchorPoint = CGPointMake(0.5, 0);
    
            // 添加渐变图层
    
            self.gradientL = [CAGradientLayer layer];
            self.gradientL.frame = self.bottomImageView.bounds;
            self.gradientL.opacity = 0;                                                 // 设置透明度
            self.gradientL.colors = @[(id)[UIColor clearColor].CGColor,
                                      (id)[UIColor blackColor].CGColor];                // 设置渐变颜色
            // self.gradientL.colors = @[(id)[UIColor redColor].CGColor,
            //                           (id)[UIColor greenColor].CGColor,
            //                           (id)[UIColor yellowColor].CGColor];
            // self.gradientL.locations = @[@0.1, @0.4, @0.5];                          // 设置渐变定位点
            // gradientL.startPoint = CGPointMake(0, 1);                                // 设置渐变开始点,取值 0~1
            [self.bottomImageView.layer addSublayer:self.gradientL];
    
            // 添加触控控件
    
            self.dragView = [[UIView alloc] initWithFrame:CGRectMake(100, 200, 200, 200)];
            UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
            [self.dragView addGestureRecognizer:pan];                                   // 添加手势
            [self.view addSubview:self.dragView];
        }
    
        // 拖动的时候旋转上部分内容
        - (void)pan:(UIPanGestureRecognizer *)pan {
    
            // 获取偏移量
            CGPoint transP = [pan translationInView:self.dragView];
    
            // 旋转角度,往下逆时针旋转
            CGFloat angle = -transP.y / 200.0 * M_PI;
    
            CATransform3D transfrom = CATransform3DIdentity;
            transfrom.m34 = -1 / 500.0;                                                 // 增加旋转的立体感,近大远小,d:距离图层的距离
            transfrom = CATransform3DRotate(transfrom, angle, 1, 0, 0);
            self.topImageView.layer.transform = transfrom;
    
            // 设置阴影效果
            self.gradientL.opacity = transP.y * 1 / 200.0;
    
            if (pan.state == UIGestureRecognizerStateEnded) {                           // 反弹
    
                // 弹簧效果的动画,SpringWithDamping:弹性系数,越小弹簧效果越明显
                [UIView animateWithDuration:0.6 delay:0 usingSpringWithDamping:0.2 
                                                         initialSpringVelocity:10 
                                                                       options:UIViewAnimationOptionCurveEaseInOut 
                                                                    animations:^{
    
                    self.topImageView.layer.transform = CATransform3DIdentity;
    
                } completion:nil];
            }
        }
    
        @end
  • 效果

    animation73

1.4 图片倒影

  • QRepView.h

        @interface QRepView : UIView
    
        @end
  • QRepView.m

        @implementation QRepView
    
        // 设置控件主层的类型
        + (Class)layerClass {
    
            return [CAReplicatorLayer class];
        }
    
        @end
  • ViewController.m

        #import "QRepView.h"
    
        @interface ViewController ()
    
        @property (nonatomic, strong) QRepView *repView;
    
        @end
    
        @implementation ViewController
    
        - (void)viewDidLoad {
            [super viewDidLoad];
    
            self.view.backgroundColor = [UIColor cyanColor];
    
            self.repView = [[QRepView alloc] initWithFrame:CGRectMake(100, 100, 150, 150)];
            [self.view addSubview:self.repView];
    
            UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.repView.bounds];
            imageView.image = [UIImage imageNamed:@"demo.jpg"];
            [self.repView addSubview:imageView];
    
            // 创建复制图层
            CAReplicatorLayer *repLayer = (CAReplicatorLayer *)self.repView.layer;
    
            // 设置子层数量
            repLayer.instanceCount = 2;
    
            // 往下面平移旋转控件
            CATransform3D translation = CATransform3DMakeTranslation(0, self.repView.bounds.size.height, 0);
            CATransform3D rotateTranslation = CATransform3DRotate(translation, M_PI, 1, 0, 0);
            repLayer.instanceTransform = rotateTranslation;
    
            // 设置阴影
            repLayer.instanceAlphaOffset = -0.1;
            repLayer.instanceRedOffset = -0.1;
            repLayer.instanceGreenOffset = -0.1;
            repLayer.instanceBlueOffset = -0.1;
        }
    
        @end
  • 效果

    animation77

1.5 音量振动条

  • 创建音量振动条

        @property (nonatomic, strong) UIView *vibrationBarView;
    
        self.vibrationBarView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 200, 200)];
        self.vibrationBarView.backgroundColor = [UIColor lightGrayColor];
        [self.view addSubview:self.vibrationBarView];
    
        // 创建复制图层
        CAReplicatorLayer *repLayer = [CAReplicatorLayer layer];                    // 可以把图层里面所有子层复制
        repLayer.frame = self.vibrationBarView.bounds;
        [self.vibrationBarView.layer addSublayer:repLayer];
    
        // 创建震动条图层
        CALayer *layer = [CALayer layer];
        layer.anchorPoint = CGPointMake(0.5, 1);
        layer.position = CGPointMake(30, self.vibrationBarView.bounds.size.height);
        layer.bounds = CGRectMake(0, 0, 30, 150);
        layer.backgroundColor = [UIColor whiteColor].CGColor;
        [repLayer addSublayer:layer];
    
        // 设置缩放动画
        CABasicAnimation *anim = [CABasicAnimation animation];
        anim.keyPath = @"transform.scale.y";
        anim.toValue = @0.1;
        anim.duration = 0.5;
        anim.repeatCount = MAXFLOAT;
        anim.autoreverses = YES;                                                    // 设置动画反转
        [layer addAnimation:anim forKey:nil];
    
        // 设置复制层中子层
        repLayer.instanceCount = 4;                                                 // 设置复制层里面有多少个子层,包括原始层
        repLayer.instanceTransform = CATransform3DMakeTranslation(45, 0, 0);        // 设置复制子层偏移量,不包括原始层,相对于原始层 x 偏移
        repLayer.instanceDelay = 0.1;                                               // 设置复制层动画延迟时间
        repLayer.instanceColor = [UIColor greenColor].CGColor;                      // 设置复制子层背景色
        repLayer.instanceGreenOffset = -0.3;
  • 效果

    animation74

1.6 活动指示器

  • 创建活动指示器

        @property (nonatomic, strong) UIView *activityIndicatorView;
    
        self.activityIndicatorView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 200, 200)];
        self.activityIndicatorView.backgroundColor = [UIColor redColor];
        [self.view addSubview:self.activityIndicatorView];
    
        // 创建复制图层
        CAReplicatorLayer *repLayer = [CAReplicatorLayer layer];                    // 可以把图层里面所有子层复制
        repLayer.frame = self.activityIndicatorView.bounds;
        [self.activityIndicatorView.layer addSublayer:repLayer];
    
        // 创建指示器图层
        CALayer *layer = [CALayer layer];
        layer.transform = CATransform3DMakeScale(0, 0, 0);
        layer.position = CGPointMake(self.activityIndicatorView.bounds.size.width / 2, 20);
        layer.bounds = CGRectMake(0, 0, 10, 10);
        layer.backgroundColor = [UIColor greenColor].CGColor;
        [repLayer addSublayer:layer];
    
        // 设置缩放动画
        CGFloat duration = 1.0;
        CABasicAnimation *anim = [CABasicAnimation animation];
        anim.keyPath = @"transform.scale";
        anim.fromValue = @1;
        anim.toValue = @0;
        anim.duration = duration;
        anim.repeatCount = MAXFLOAT;
        [layer addAnimation:anim forKey:nil];
    
        // 设置复制层中子层
        CGFloat count = 20;
        CGFloat angle = M_PI * 2 / count;
        repLayer.instanceCount = count;                                             // 设置复制层里面有多少个子层,包括原始层
        repLayer.instanceTransform = CATransform3DMakeRotation(angle, 0, 0, 1);     // 设置复制子层偏移量,不包括原始层,相对于原始层 x 偏移
        repLayer.instanceDelay = duration / count;                                  // 设置复制层动画延迟时间
  • 效果

    animation75

1.7 粒子运动效果

  • QParticleMotionView.h

        @interface QParticleMotionView : UIView
    
        /// 创建粒子运动效果界面
        + (instancetype)q_particleMotionViewWithFrame:(CGRect)frame;
    
        /// 开始运动
        - (void)q_startAnimating;
    
        /// 清除运动
        - (void)q_clearAnimating;
    
        @end
  • QParticleMotionView.m

        @interface QParticleMotionView ()
    
        /// 贝塞尔路径
        @property (nonatomic, strong) UIBezierPath *path;
    
        /// 复制图层
        @property (nonatomic, strong) CAReplicatorLayer *repLayer;
    
        /// 圆点图层
        @property (nonatomic, strong) CALayer *dotLayer;
    
        /// 圆点个数
        @property (nonatomic, assign) NSInteger dotCount;
    
        @end
    
        @implementation QParticleMotionView
    
        /// 创建粒子运动效果界面
        + (instancetype)q_particleMotionViewWithFrame:(CGRect)frame {
    
            QParticleMotionView *particleMotionView = [[self alloc] initWithFrame:frame];
            particleMotionView.backgroundColor = [UIColor whiteColor];
            return particleMotionView;
        }
    
        /// 开始运动
        - (void)q_startAnimating {
    
            // 创建关键帧动画
            CAKeyframeAnimation *anim = [CAKeyframeAnimation animation];
            anim.keyPath = @"position";
            anim.path = self.path.CGPath;
            anim.duration = 3;
            anim.repeatCount = MAXFLOAT;
            [self.dotLayer addAnimation:anim forKey:@"dotAnimation"];
    
            // 复制子层
            self.repLayer.instanceCount = self.dotCount;
            self.repLayer.instanceDelay = 0.2;
        }
    
        /// 清除运动
        - (void)q_clearAnimating {
    
            // 移除动画
            [self.dotLayer removeAnimationForKey:@"dotAnimation"];
    
            // 清空路径
            self.path = nil;
    
            // 清空子层总数
            self.dotCount = 0;
    
            [self setNeedsDisplay];
        }
    
        /// 初始化
        - (instancetype)initWithFrame:(CGRect)frame {
    
            if (self = [super initWithFrame:frame]) {
    
                self.dotCount = 0;
    
                // 创建复制图层
                self.repLayer = [CAReplicatorLayer layer];
                self.repLayer.frame = self.bounds;
                [self.layer addSublayer:self.repLayer];
    
                // 创建圆点图层
                CGFloat wh = 10;
                self.dotLayer = [CALayer layer];
                self.dotLayer.frame = CGRectMake(0, -1000, wh, wh);
                self.dotLayer.cornerRadius = wh / 2;
                self.dotLayer.backgroundColor = [UIColor blueColor].CGColor;
                [self.repLayer addSublayer:self.dotLayer];
            }
            return self;
        }
    
        /// 触摸开始
        - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {
    
            // 获取触摸起始点位置
            CGPoint startPoint = [touches.anyObject locationInView:self];
    
            // 设置起点
            [self.path moveToPoint:startPoint];
        }
    
        /// 触摸移动
        - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {
    
            // 获取触摸点位置
            CGPoint touchPoint = [touches.anyObject locationInView:self];
    
            // 添加线到某个点
            [self.path addLineToPoint:touchPoint];
    
            // 重绘
            [self setNeedsDisplay];
    
            self.dotCount ++;
        }
    
        /// 绘制贝塞尔路径
        - (void)drawRect:(CGRect)rect {
    
            [self.path stroke];
        }
    
        /// 懒加载路径
        - (UIBezierPath *)path {
    
            if (_path == nil) {
                _path = [UIBezierPath bezierPath];
            }
            return _path;
        }
    
        @end
  • ViewController.m

        @property (nonatomic, strong) QParticleMotionView *particleMotionView;
    
        // 创建粒子运动效果界面
        CGRect frame = CGRectMake(0, 100, self.view.bounds.size.width, self.view.bounds.size.height - 100);
        self.particleMotionView = [QParticleMotionView q_particleMotionViewWithFrame:frame];
        [self.view addSubview:self.particleMotionView];
    
        // 开始运动
        [self.particleMotionView q_startAnimating];
    
        // 清除运动
        [self.particleMotionView q_clearAnimating];
  • 效果

    animation76

1.8 划定路径移动

  • DrawView.m

        @interface DrawView ()
    
        @property (nonatomic, strong) UIBezierPath *path;
    
        @end
    
        @implementation DrawView
    
        /// 触摸开始
        - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {
    
            // 移除上一个动画
            [self.subviews.firstObject.layer removeAnimationForKey:@"drawAnimation"];
    
            // 获取触摸起始点位置
            CGPoint startPoint = [touches.anyObject locationInView:self];
    
            // 创建路径
            self.path = [UIBezierPath bezierPath];
    
            // 设置起点
            [self.path moveToPoint:startPoint];
        }
    
        /// 触摸移动
        - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {
    
            // 获取触摸点位置
            CGPoint touchPoint = [touches.anyObject locationInView:self];
    
            [self.path addLineToPoint:touchPoint];
    
            [self setNeedsDisplay];
        }
    
        /// 触摸结束
        - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {
    
            // 添加关键帧动画
    
            CAKeyframeAnimation *anim = [CAKeyframeAnimation animation];
            anim.keyPath = @"position";
    
            // 添加动画路径
            anim.path = self.path.CGPath;
    
            anim.duration = 3;
            anim.repeatCount = MAXFLOAT;
            anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    
            // 给子视图添加核心动画
            [self.subviews.firstObject.layer addAnimation:anim forKey:@"drawAnimation"];
        }
    
        /// 触摸取消
        - (void)touchesCancelled:(NSSet *)touches withEvent:(nullable UIEvent *)event {
            [self touchesEnded:touches withEvent:event];
        }
    
        /// 绘图
        - (void)drawRect:(CGRect)rect {
    
            [self.path stroke];
        }
    
        @end
  • ViewController.m

        DrawView *myDrawView = [[DrawView alloc] initWithFrame:self.view.bounds];
        myDrawView.backgroundColor = [UIColor whiteColor];
        [self.view addSubview:myDrawView];
    
        UIImageView *imv = [[UIImageView alloc] initWithFrame:CGRectMake(100, 100, 50, 50)];
        imv.image = [UIImage imageNamed:@"heart2"];
        [myDrawView addSubview:imv];
  • 效果

    animation47

1.9 粘性效果按钮

  • QGooButton.h

        @interface QGooButton : UIButton
    
        @end
  • QGooButton.m

        /// 最大圆心距离
        #define MAX_DISTANCE    80
    
        #define WIDTH           self.bounds.size.width
    
        @interface QGooButton ()
    
        /// 小圆视图
        @property (nonatomic, strong) UIView *smallCircleView;
    
        /// 不规则矩形
        @property (nonatomic, strong) CAShapeLayer *shapeLayer;
    
        /// 小圆原始半径
        @property (nonatomic, assign) CGFloat oriSmallRadius;
    
        /// 小圆原始中心
        @property (nonatomic, assign) CGPoint oriSmallCenter;
    
        @end
    
        @implementation QGooButton
    
        /// 懒加载
    
        - (CAShapeLayer *)shapeLayer {
    
            if (_shapeLayer == nil) {
    
                // 展示不规则矩形,通过不规则矩形路径生成一个图层
                _shapeLayer = [CAShapeLayer layer];
                _shapeLayer.fillColor = self.backgroundColor.CGColor;
    
                [self.superview.layer insertSublayer:_shapeLayer below:self.layer];
            }
            return _shapeLayer;
        }
    
        - (UIView *)smallCircleView {
    
            if (_smallCircleView == nil) {
    
                _smallCircleView = [[UIView alloc] init];
                _smallCircleView.backgroundColor = self.backgroundColor;
    
                // 小圆添加按钮的父控件上
                [self.superview insertSubview:_smallCircleView belowSubview:self];
            }
            return _smallCircleView;
        }
    
        /// 初始化
        - (instancetype)initWithFrame:(CGRect)frame {
    
            if (self = [super initWithFrame:frame]) {
    
                // 记录小圆最初始半径和中心
                self.oriSmallRadius = WIDTH / 2;
    
                // 设置按钮圆角
                self.layer.cornerRadius = WIDTH / 2;
    
                // 设置按钮文字颜色和字体大小
                [self setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
                self.titleLabel.font = [UIFont systemFontOfSize:18];
    
                // 添加手势
                UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
                [self addGestureRecognizer:pan];
            }
            return self;
        }
    
        - (void)layoutSubviews {
    
            [super layoutSubviews];
    
            static int flag = 1;
    
            if (flag) {
    
                flag = 0;
    
                // 设置小圆位置和尺寸
                self.smallCircleView.center = self.center;
                self.smallCircleView.bounds = self.bounds;
                self.smallCircleView.layer.cornerRadius = WIDTH / 2;
            }
        }
    
        /// 拖动手势事件处理
        - (void)pan:(UIPanGestureRecognizer *)pan {
    
            // 获取手指偏移量
            CGPoint transP = [pan translationInView:self];
    
            // 移动控件位置
            CGPoint center = self.center;
            center.x += transP.x;
            center.y += transP.y;
            self.center = center;
    
            // 复位
            [pan setTranslation:CGPointZero inView:self];
    
            // 显示后面圆,后面圆的半径,随着两个圆心的距离不断增加而减小
    
            // 计算圆心距离
            CGFloat d = [self circleCenterDistanceWithBigCircleCenter:self.center smallCircleCenter:self.smallCircleView.center];
    
            // 计算小圆的半径
            CGFloat smallRadius = self.oriSmallRadius - d / 10;
    
            // 设置小圆的尺寸
            self.smallCircleView.bounds = CGRectMake(0, 0, smallRadius * 2, smallRadius * 2);
            self.smallCircleView.layer.cornerRadius = smallRadius;
    
            // 当圆心距离大于最大圆心距离
            if (d > MAX_DISTANCE) {                                         // 可以拖出来
    
                // 隐藏小圆
                self.smallCircleView.hidden = YES;
    
                // 移除不规则的矩形
                [self.shapeLayer removeFromSuperlayer];
                self.shapeLayer = nil;
    
            } else if (d > 0 && self.smallCircleView.hidden == NO) {        // 有圆心距离,并且圆心距离不大,才需要展示
    
                // 展示不规则矩形,通过不规则矩形路径生成一个图层
    
                self.shapeLayer.path = [self pathWithBigCirCleView:self smallCirCleView:self.smallCircleView].CGPath;
            }
    
            // 手指抬起的时候
            if (pan.state == UIGestureRecognizerStateEnded) {
    
                // 当圆心距离大于最大圆心距离
                if (d > MAX_DISTANCE) {
    
                    // 展示 gif 动画
    
                    UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.bounds];
                    NSMutableArray *arrM = [NSMutableArray array];
                    for (int i = 1; i < 9; i++) {
                        UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:@"%d",i]];
                        [arrM addObject:image];
                    }
                    imageView.animationImages = arrM;
                    imageView.animationRepeatCount = 1;
                    imageView.animationDuration = 0.5;
                    [imageView startAnimating];
                    [self addSubview:imageView];
                    self.backgroundColor = [UIColor clearColor];
    
                    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                        [self removeFromSuperview];
                    });
    
                } else {
    
                    // 移除不规则矩形,还原
    
                    [self.shapeLayer removeFromSuperlayer];
                    self.shapeLayer = nil;
    
                    // 还原位置
                    [UIView animateWithDuration:0.5 delay:0 usingSpringWithDamping:0.2 
                                                             initialSpringVelocity:0 
                                                                           options:UIViewAnimationOptionCurveLinear 
                                                                        animations:^{
    
                        // 设置大圆中心点位置
                        self.center = self.smallCircleView.center;
    
                    } completion:^(BOOL finished) {
    
                        // 显示小圆
                        self.smallCircleView.hidden = NO;
                    }];
                }
            }
        }
    
        /// 计算两个圆心之间的距离
        - (CGFloat)circleCenterDistanceWithBigCircleCenter:(CGPoint)bigCircleCenter smallCircleCenter:(CGPoint)smallCircleCenter {
    
            CGFloat offsetX = bigCircleCenter.x - smallCircleCenter.x;
            CGFloat offsetY = bigCircleCenter.y - smallCircleCenter.y;
    
            return sqrt(offsetX * offsetX + offsetY * offsetY);
        }
    
        /// 描述两圆之间一条矩形路径
        - (UIBezierPath *)pathWithBigCirCleView:(UIView *)bigCirCleView  smallCirCleView:(UIView *)smallCirCleView {
    
            CGPoint bigCenter = bigCirCleView.center;
            CGFloat x2 = bigCenter.x;
            CGFloat y2 = bigCenter.y;
            CGFloat r2 = bigCirCleView.bounds.size.width / 2;
    
            CGPoint smallCenter = smallCirCleView.center;
            CGFloat x1 = smallCenter.x;
            CGFloat y1 = smallCenter.y;
            CGFloat r1 = smallCirCleView.bounds.size.width / 2;
    
            // 获取圆心距离
            CGFloat d = [self circleCenterDistanceWithBigCircleCenter:bigCenter smallCircleCenter:smallCenter];
    
            CGFloat sinθ = (x2 - x1) / d;
    
            CGFloat cosθ = (y2 - y1) / d;
    
            // 坐标系基于父控件
            CGPoint pointA = CGPointMake(x1 - r1 * cosθ , y1 + r1 * sinθ);
            CGPoint pointB = CGPointMake(x1 + r1 * cosθ , y1 - r1 * sinθ);
            CGPoint pointC = CGPointMake(x2 + r2 * cosθ , y2 - r2 * sinθ);
            CGPoint pointD = CGPointMake(x2 - r2 * cosθ , y2 + r2 * sinθ);
            CGPoint pointO = CGPointMake(pointA.x + d / 2 * sinθ , pointA.y + d / 2 * cosθ);
            CGPoint pointP = CGPointMake(pointB.x + d / 2 * sinθ , pointB.y + d / 2 * cosθ);
    
            UIBezierPath *path = [UIBezierPath bezierPath];
    
            // A
            [path moveToPoint:pointA];
    
            // AB
            [path addLineToPoint:pointB];
    
            // 绘制 BC 曲线
            [path addQuadCurveToPoint:pointC controlPoint:pointP];
    
            // CD
            [path addLineToPoint:pointD];
    
            // 绘制 DA 曲线
            [path addQuadCurveToPoint:pointA controlPoint:pointO];
    
            return path;
        }
    
        @end

    animation78

  • ViewController.m

        // 创建粘性按钮
        QGooButton *gooButton = [[QGooButton alloc] initWithFrame:CGRectMake(200, 200, 50, 50)];
    
        [gooButton setBackgroundColor:[UIColor redColor]];
        [gooButton setTitle:@"10" forState:UIControlStateNormal];
    
        [self.view addSubview:gooButton];
  • 效果

    animation79

1.10 启动动画

  • QWelcomeView.h

        @interface QWelcomeView : UIView
    
        /// 创建欢迎视图
        + (instancetype)q_weicomeView;
    
        @end
  • QWelcomeView.m

        @interface QWelcomeView ()
    
        @property (weak, nonatomic) IBOutlet UIImageView *backView;
    
        @property (weak, nonatomic) IBOutlet UIImageView *sloganView;
    
        @property (weak, nonatomic) IBOutlet UIImageView *iconView;
    
        @property (weak, nonatomic) IBOutlet UILabel *textView;
    
        @end
    
        @implementation QWelcomeView
    
        /// 创建欢迎视图
        + (instancetype)q_weicomeView{
            return [[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self)
                                                 owner:nil
                                               options:nil].firstObject;
        }
    
        /// 初始化
        - (void)awakeFromNib {
            [super awakeFromNib];
    
            self.backView.image = [UIImage imageNamed:@"ad_background"];
    
            self.sloganView.image = [UIImage imageNamed:@"compose_slogan"];
            self.iconView.image = [UIImage imageNamed:@"QianChia0123"];
    
            self.textView.text = @"欢迎回来";
        }
    
        /// 已经添加到父视图上
        - (void)didMoveToSuperview {
    
            [super didMoveToSuperview];
    
            // 设置头像圆角
            self.iconView.layer.cornerRadius = 50;
            self.iconView.layer.masksToBounds = YES;
    
            // 头像下移
            self.iconView.transform = CGAffineTransformMakeTranslation(0, 50);
    
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    
                [UIView animateWithDuration:1.0 animations:^{
    
                    // 文字图片慢慢消失
                    self.sloganView.alpha = 0;
    
                } completion:^(BOOL finished) {
    
                    // 显示头像
                    self.iconView.hidden = NO;
    
                    [UIView animateWithDuration:1.0 delay:0 usingSpringWithDamping:0.3
                                                             initialSpringVelocity:0
                                                                           options:UIViewAnimationOptionCurveLinear
                                                                        animations:^{
                        // 头像往上移动的动画,弹簧效果
                        self.iconView.transform = CGAffineTransformIdentity;
    
                    } completion:^(BOOL finished) {
    
                        self.textView.alpha = 0;
                        self.textView.hidden = NO;
    
                        // 文字慢慢显示
    
                        [UIView animateWithDuration:0.5 animations:^{
    
                            // 欢迎回来 的文字慢慢显示
                            self.textView.alpha = 1;
    
                        } completion:^(BOOL finished) {
    
                            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)),
                                           dispatch_get_main_queue(), ^{
    
                                // 移除
                                [self removeFromSuperview];
                            });
                        }];
                    }];
                }];
            });
        }
    
        @end
  • AppDelegate.m

        #import "QWelcomeView.h"
    
        - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
            // 创建窗口
            self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
            UIStoryboard *stroyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
            UIViewController *vc = [stroyboard instantiateInitialViewController];
            self.window.rootViewController = vc;
            [self.window makeKeyAndVisible];
    
            // 显示欢迎界面
            QWelcomeView *welcomeV = [QWelcomeView q_weicomeView];
            welcomeV.frame = self.window.bounds;                // 注意:一定要给界面设置 Frame
            [self.window addSubview:welcomeV];
    
            return YES;
        }
  • 效果

    animation82animation80

1.11 菜单按钮动画

  • QMenuItemModel.h

        @interface QMenuItemModel : NSObject
    
        @property (nonatomic, strong) NSString *title;
        @property (nonatomic, strong) UIImage *image;
    
        + (instancetype)q_menuItemWithTitle:(NSString *)title image:(UIImage *)image;
    
        @end
  • QMenuItemModel.m

        @implementation QMenuItemModel
    
        + (instancetype)q_menuItemWithTitle:(NSString *)title image:(UIImage *)image {
    
            QMenuItemModel *itme = [[self alloc] init];
    
            itme.title = title;
            itme.image = image;
    
            return itme;
        }
    
        @end
  • QMenuItemButton.h

        @interface QMenuItemButton : UIButton
    
        @end
  • QMenuItemButton.m

        #define kImageRatio 0.8
    
        @implementation QMenuItemButton
    
        - (void)awakeFromNib {
            [super awakeFromNib];
    
            [self setUp];
        }
    
        - (instancetype)initWithFrame:(CGRect)frame {
    
            if (self = [super initWithFrame:frame]) {
                [self setUp];
            }
            return self;
        }
    
        - (void)setUp {
    
            self.imageView.contentMode = UIViewContentModeCenter;
            self.titleLabel.textAlignment = NSTextAlignmentCenter;
    
            [self setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
        }
    
        /// 布局子控件
        - (void)layoutSubviews {
            [super layoutSubviews];
    
            // UIImageView
            CGFloat imageX = 0;
            CGFloat imageY = 0;
            CGFloat imageW = self.bounds.size.width;
            CGFloat imageH = self.bounds.size.height * kImageRatio;
            self.imageView.frame = CGRectMake(imageX, imageY, imageW, imageH);
    
            // UILabel
            CGFloat labelY = imageH;
            CGFloat labelH = self.bounds.size.height - labelY;
            self.titleLabel.frame = CGRectMake(imageX, labelY, imageW, labelH);
        }
    
        @end
  • QComposeItemViewController.h

        @interface QComposeItemViewController : UIViewController
    
        @end
  • QComposeItemViewController.m

        #import "QMenuItemModel.h"
        #import "QMenuItemButton.h"
    
        @interface QComposeItemViewController ()
    
        @property (nonatomic, strong) NSArray *items;
    
        @property (nonatomic, strong) NSTimer *timer;
    
        @property (nonatomic, assign) int btnIndex;
    
        @property (nonatomic, strong) NSMutableArray *itemButtons;
    
        @end
    
        @implementation QComposeItemViewController
    
        ///
        - (void)viewDidLoad {
            [super viewDidLoad];
    
            // 添加所有 item 按钮
            [self setUpAllBtns];
    
            // 按钮按顺序的从下往上偏移
            self.timer = [NSTimer scheduledTimerWithTimeInterval:0.1
                                                          target:self
                                                        selector:@selector(timeChange)
                                                        userInfo:nil
                                                         repeats:YES];
        }
    
        /// 添加所有 item 按钮
        - (void)setUpAllBtns {
    
            int cols = 3;
            int col = 0;
            int row = 0;
            CGFloat x = 0;
            CGFloat y = 0;
            CGFloat wh = 100;
            CGFloat margin = ([UIScreen mainScreen].bounds.size.width - cols * wh) / (cols + 1);
            CGFloat oriY = 300;
    
            for (int i = 0; i < self.items.count; i++) {
    
                UIButton *btn = [QMenuItemButton buttonWithType:UIButtonTypeCustom];
    
                col = i % cols;
                row = i / cols;
    
                x = margin + col * (margin + wh);
                y = row * (margin + wh) + oriY;
    
                btn.frame = CGRectMake(x, y, wh, wh);
    
                // 设置按钮的图片和文字
                QMenuItemModel *item = self.items[i];
    
                [btn setImage:item.image forState:UIControlStateNormal];
                [btn setTitle:item.title forState:UIControlStateNormal];
    
                // 偏移到底部
                btn.transform = CGAffineTransformMakeTranslation(0, self.view.bounds.size.height);
    
                [btn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchDown];
                [btn addTarget:self action:@selector(btnClick1:) forControlEvents:UIControlEventTouchUpInside];
    
                [self.itemButtons addObject:btn];
    
                [self.view addSubview:btn];
            }
        }
    
        /// 给所有的按钮做动画
        - (void)setUpAllBtnAnim {
    
            for (UIButton *btn in self.itemButtons) {
                [self setUpOneBtnAnim:btn];
            }
        }
    
        /// 给一个按钮做动画
        - (void)setUpOneBtnAnim:(UIButton *)btn {
    
            [UIView animateWithDuration:0.8 delay:0 usingSpringWithDamping:0.7
                                                     initialSpringVelocity:0
                                                                   options:UIViewAnimationOptionCurveEaseIn
                                                                animations:^{
                btn.transform = CGAffineTransformIdentity;
    
            } completion:nil];
        }
    
        /// 定时器响应事件处理
        - (void)timeChange {
    
            // 让一个按钮做动画
    
            if (self.btnIndex == self.itemButtons.count) {
    
                // 定时器停止
                [self.timer invalidate];
    
                return;
            }
    
            // 获取按钮
            UIButton *btn = self.itemButtons[self.btnIndex];
    
            [self setUpOneBtnAnim:btn];
    
            self.btnIndex++;
        }
    
        /// 按钮按下响应事件处理
        - (void)btnClick:(UIButton *)btn {
    
            [UIView animateWithDuration:0.5 animations:^{
    
                btn.transform = CGAffineTransformMakeScale(1.2, 1.2);
            }];
        }
    
        /// 按钮点击响应事件处理
        - (void)btnClick1:(UIButton *)btn {
    
            [UIView animateWithDuration:0.5 animations:^{
    
                btn.transform = CGAffineTransformMakeScale(2.0, 2.0);
                btn.alpha = 0;
            }];
    
            NSLog(@"%s", __func__);
        }
    
        /// 懒加载
    
        - (NSArray *)items {
    
            if (_items == nil) {
    
                // 创建模型对象
                QMenuItemModel *item1 = [QMenuItemModel q_menuItemWithTitle:@"点评"
                                                                      image:[UIImage imageNamed:@"tabbar_compose_review"]];
                QMenuItemModel *item2 = [QMenuItemModel q_menuItemWithTitle:@"更多"
                                                                      image:[UIImage imageNamed:@"tabbar_compose_more"]];
                QMenuItemModel *item3 = [QMenuItemModel q_menuItemWithTitle:@"拍摄"
                                                                      image:[UIImage imageNamed:@"tabbar_compose_camera"]];
                QMenuItemModel *item4 = [QMenuItemModel q_menuItemWithTitle:@"相册"
                                                                      image:[UIImage imageNamed:@"tabbar_compose_photo"]];
                QMenuItemModel *item5 = [QMenuItemModel q_menuItemWithTitle:@"文字"
                                                                      image:[UIImage imageNamed:@"tabbar_compose_idea"]];
                QMenuItemModel *item6 = [QMenuItemModel q_menuItemWithTitle:@"签到"
                                                                      image:[UIImage imageNamed:@"tabbar_compose_review"]];
    
                _items = @[item1, item2, item3, item4, item5, item6];
            }
            return _items;
        }
    
        - (NSMutableArray *)itemButtons {
    
            if (_itemButtons == nil) {
                _itemButtons = [NSMutableArray array];
            }
            return _itemButtons;
        }
    
        @end
  • ViewController.m

        // 点击加号按钮
        - (IBAction)btnClick:(id)sender {
    
            QComposeItemViewController *vc = [[QComposeItemViewController alloc] init];
    
            [self presentViewController:vc animated:YES completion:nil];
        }
  • 效果

    animation81

1.12 引导页动画

  • ViewController.m

        @interface ViewController () <UIScrollViewDelegate>
    
        @property (weak, nonatomic) IBOutlet UIImageView *sunView;
        @property (weak, nonatomic) IBOutlet UIImageView *personView;
    
        @end
    
        @implementation ViewController
    
        - (void)viewDidLoad {
            [super viewDidLoad];
    
            UIImage *bgImage = [UIImage imageNamed:@"520_userguid_bg"];
    
            // ScrollView
            UIScrollView *scorollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
            scorollView.contentSize = bgImage.size;
            scorollView.delegate = self;
            scorollView.decelerationRate = 0.5;
            [self.view insertSubview:scorollView atIndex:0];
    
            // bg
            UIImageView *bgImageView = [[UIImageView alloc] initWithImage:bgImage];
            CGRect rect = bgImageView.frame;
            rect.size.height = self.view.bounds.size.height;
            bgImageView.frame = rect;
            [scorollView addSubview:bgImageView];
    
            // cg
            UIImage *cgImage = [UIImage imageNamed:@"520_userguid_cg"];
            UIImageView *cgImageView = [[UIImageView alloc] initWithImage:cgImage];
            rect = cgImageView.frame;
            rect.size.height = self.view.bounds.size.height;
            cgImageView.frame = rect;
            [bgImageView addSubview:cgImageView];
    
            // fg
            UIImage *fgImage = [UIImage imageNamed:@"520_userguid_fg"];
            UIImageView *fgImageView = [[UIImageView alloc] initWithImage:fgImage];
            rect = cgImageView.frame;
            rect.size.height = self.view.bounds.size.height;
            fgImageView.frame = rect;
            [bgImageView addSubview:fgImageView];
    
            // cloud
            UIImage *cloudImage = [UIImage imageNamed:@"520_userguid_cloud"];
            UIImageView *cloudImageView = [[UIImageView alloc] initWithImage:cloudImage];
            [bgImageView addSubview:cloudImageView];
        }
    
        // 只要一滚动就会调用
        - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    
            // 获取 scrollView 偏移量
            CGFloat offsetX = scrollView.contentOffset.x;
    
            int intOffsetX = (int)offsetX;
    
            // 切换人物的图片
            NSString *imageName = [NSString stringWithFormat:@"520_userguid_person_taitou_%d", (intOffsetX % 2 + 1)];
            self.personView.image = [UIImage imageNamed:imageName];
    
            // 旋转小太阳
            self.sunView.transform = CGAffineTransformRotate(self.sunView.transform, 5 / 180.0 * M_PI);
        }
    
        @end
  • 效果

    animation85animation84

目录
相关文章
|
6月前
|
算法 计算机视觉 iOS开发
iOS 实时图像处理技术:使用 Core Image 和 Metal 进行高效滤镜应用
【4月更文挑战第8天】 在移动设备上实现高效的图像处理功能是现代应用程序开发中的一个关键需求。苹果的iOS平台提供了Core Image和Metal两大技术,它们为开发者提供了强大的工具来实现复杂的图像处理任务。本文将探讨如何使用Core Image进行基础图像处理,并结合Metal的性能优势,开发出一个自定义的实时图像滤镜。我们将通过创建一个能够动态调整参数并且具有实时反馈效果的滤镜来演示这一过程。
|
6月前
|
算法 计算机视觉 iOS开发
iOS 实时图像处理技术:Core Image 框架的应用
【4月更文挑战第8天】 在移动设备上实现高效的图像处理功能,对于提升用户体验和扩展应用程序能力至关重要。苹果公司的iOS平台提供了强大的Core Image框架,它允许开发者以高效和直观的方式执行复杂的图像处理任务。本文将深入探讨Core Image框架的关键特性,并通过实例演示如何在iOS应用中集成实时图像处理功能,不仅提高性能,同时保持了电池寿命的优化。我们将重点讨论面部识别、滤镜应用和性能优化等关键技术点,为读者提供一份全面的iOS图像处理指南。
|
6月前
|
存储 数据建模 iOS开发
iOS设备功能和框架: 什么是 Core Data,它在 iOS 中的作用是什么?
iOS设备功能和框架: 什么是 Core Data,它在 iOS 中的作用是什么?
101 1
|
6月前
|
定位技术 iOS开发
iOS设备功能和框架: 如何使用 Core Location 获取设备的位置信息?
iOS设备功能和框架: 如何使用 Core Location 获取设备的位置信息?
75 0
|
编译器 iOS开发 异构计算
读iOS核心动画笔记
读iOS核心动画笔记
55 0
|
23天前
|
Swift iOS开发 UED
如何使用Swift和UIKit在iOS应用中实现自定义按钮动画
本文通过一个具体案例,介绍如何使用Swift和UIKit在iOS应用中实现自定义按钮动画。当用户点击按钮时,按钮将从圆形变为椭圆形,颜色从蓝色渐变到绿色;释放按钮时,动画以相反方式恢复。通过UIView的动画方法和弹簧动画效果,实现平滑自然的过渡。
40 1
|
1月前
|
Swift iOS开发 UED
如何使用Swift和UIKit在iOS应用中实现自定义按钮动画
【10月更文挑战第18天】本文通过一个具体案例,介绍如何使用Swift和UIKit在iOS应用中实现自定义按钮动画。当用户按下按钮时,按钮将从圆形变为椭圆形并从蓝色渐变为绿色;释放按钮时,动画恢复原状。通过UIView的动画方法和弹簧动画效果,实现平滑自然的动画过渡。
50 5
|
2月前
|
Swift iOS开发 UED
揭秘一款iOS应用中令人惊叹的自定义动画效果,带你领略编程艺术的魅力所在!
【9月更文挑战第5天】本文通过具体案例介绍如何在iOS应用中使用Swift与UIKit实现自定义按钮动画,当用户点击按钮时,按钮将从圆形变为椭圆形并从蓝色渐变到绿色,释放后恢复原状。文中详细展示了代码实现过程及动画平滑过渡的技巧,帮助读者提升应用的视觉体验与特色。
63 11
|
3月前
|
Swift iOS开发 UED
【绝妙创意】颠覆你的视觉体验!揭秘一款iOS应用中令人惊叹的自定义动画效果,带你领略编程艺术的魅力所在!
【8月更文挑战第13天】本文通过一个具体案例,介绍如何使用Swift与UIKit在iOS应用中创建独特的按钮动画效果。当按钮被按下时,其形状从圆形变化为椭圆形,颜色则从蓝色渐变为绿色;释放后,动画反向恢复原状。利用UIView动画方法及弹簧动画效果,实现了平滑自然的过渡。通过调整参数,开发者可以进一步优化动画体验,增强应用的互动性和视觉吸引力。
52 7
|
6月前
|
机器学习/深度学习 PyTorch TensorFlow
iOS设备功能和框架: 什么是 Core ML?如何在应用中集成机器学习模型?
iOS设备功能和框架: 什么是 Core ML?如何在应用中集成机器学习模型?
182 0