中秋征文活动· iOS 实现-《中秋主题系列》 月饼券 红包雨

772 阅读2分钟

我正在参加中秋创意投稿大赛,详情请看:中秋创意投稿大赛

实现效果:


2021-09-17 17.25.27.gif

思路:

1、生成一个红包layer

  self.animationLayer = [CALayer new];
  self.animationLayer.bounds = imageV.frame;
  self.animationLayer.anchorPoint = CGPointMake(0, 0);
  self.animationLayer.position = CGPointMake(0, -52.5 );
  self.animationLayer.contents = (id)imageV.image.CGImage;
  [self.touchView.layer addSublayer:self.animationLayer];

2、添加layer的动画

 - (void)addAnimation
    {
        CAKeyframeAnimation * moveAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
        NSValue * A = [NSValue valueWithCGPoint:CGPointMake(arc4random() % 414, 0)];
        NSValue * B = [NSValue valueWithCGPoint:CGPointMake(arc4random() % 414, [UIScreen mainScreen].bounds.size.height)];
        moveAnimation.values = @[A,B];
        moveAnimation.duration = arc4random() % 200 / 100.0 + 3.5;
        moveAnimation.repeatCount = 1;
        moveAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
        [self.animationLayer addAnimation:moveAnimation forKey:nil];
    
        CAKeyframeAnimation * tranAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
        CATransform3D r0 = CATransform3DMakeRotation(M_PI/180 * (arc4random() % 360 ) , 0, 0, -1);
        CATransform3D r1 = CATransform3DMakeRotation(M_PI/180 * (arc4random() % 360 ) , 0, 0, -1);
        tranAnimation.values = @[[NSValue valueWithCATransform3D:r0],[NSValue valueWithCATransform3D:r1]];
        tranAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
        tranAnimation.duration = arc4random() % 200 / 100.0 + 3.5;
        //为了避免旋转动画完成后再次回到初始状态。
        [tranAnimation setFillMode:kCAFillModeForwards];
        [tranAnimation setRemovedOnCompletion:NO];
        [self.animationLayer addAnimation:tranAnimation forKey:nil];
    }

3、定时生成红包

   - (void)startRedPackerts
    {
    
        if (self.timer) {
            [self.timer invalidate];
        }
        self.timer= nil;
    
        self.timer = [NSTimer scheduledTimerWithTimeInterval:(1/2.0) target:self selector:@selector(showRain) userInfo:nil repeats:YES];
    }

4、判断红包点击事件

    - (void)clickRed:(UITapGestureRecognizer *)sender
    {
        CGPoint point = [sender locationInView:self.touchView];
        for (int i = 0 ; i < self.touchView.layer.sublayers.count ; i ++)
        {
            CALayer * layer = self.touchView.layer.sublayers[i];
            if ([[layer presentationLayer] hitTest:point] != nil)
            {
                NSLog(@"%d",i);
            
                BOOL hasRedPacketd = !(i % 2) ;
            
                UIImageView * newPacketIV = [UIImageView new];
                if (hasRedPacketd)
                {
                    newPacketIV.image = [UIImage imageNamed:@"moon_cake"];
                    newPacketIV.backgroundColor = [UIColor redColor];
                
                    newPacketIV.frame = CGRectMake(0, 0, 63.5, 74);
                }
                else
                {
                    newPacketIV.image = [UIImage imageNamed:@"moon_cry"];
                    newPacketIV.backgroundColor = [UIColor greenColor];
    
                    newPacketIV.frame = CGRectMake(0, 0, 45.5, 76.5);
                }
            
                layer.contents = (id)newPacketIV.image.CGImage;
            
                UIView * alertView = [UIView new];
                alertView.layer.cornerRadius = 5;
                alertView.frame = CGRectMake(point.x - 50, point.y, 100, 30);
                [self.touchView addSubview:alertView];
            
                UILabel * label = [UILabel new];
                label.font = [UIFont systemFontOfSize:17];
            
                if (!hasRedPacketd)
                {
                    label.text = @"嘤嘤嘤!旺旺旺";
                    label.textColor = [UIColor blackColor];
                }
                else
                {
                    NSString * string = [NSString stringWithFormat:@"+%d月饼券",i];
                    NSString * iString = [NSString stringWithFormat:@"%d",i];
                    NSMutableAttributedString * attributedStr = [[NSMutableAttributedString alloc]initWithString:string];
                
                    [attributedStr addAttribute:NSFontAttributeName
                                      value:[UIFont systemFontOfSize:27]
                                      range:NSMakeRange(0, 1)];
                    [attributedStr addAttribute:NSFontAttributeName
                                      value:[UIFont fontWithName:@"PingFangTC-Semibold" size:32]
                                      range:NSMakeRange(1, iString.length)];
                    [attributedStr addAttribute:NSFontAttributeName
                                      value:[UIFont systemFontOfSize:17]
                                      range:NSMakeRange(1 + iString.length, 2)];
                    label.attributedText = attributedStr;
                    label.textColor = [UIColor blueColor];
                }
            
                [alertView addSubview:label];
                [label mas_makeConstraints:^(MASConstraintMaker *make) {
                    make.centerX.equalTo(alertView.mas_centerX);
                    make.centerY.equalTo(alertView.mas_centerY);
                }];
            
                [UIView animateWithDuration:1 animations:^{
                    alertView.alpha = 0;
                    alertView.frame = CGRectMake(point.x- 50, point.y - 100, 100, 30);
                } completion:^(BOOL finished) {
                    [alertView removeFromSuperview];
                }];
            }
        }
    }


5、停止红包雨

- (void)endAnimation
    {
        [self.timer invalidate];
    
        for (NSInteger i = 0; i < self.touchView.layer.sublayers.count ; i ++)
        {
            CALayer * layer = self.touchView.layer.sublayers[i];
            [layer removeAllAnimations];
        }
    }

总结,注意事项:

  1. 倒计时用 NSTimer实现。
  2. 红包雨的动画用CALayer来实现。。
  3. 动画分为两个部分,第一个部分是位移动画,第二部分是旋转动画。
  4. 红包下落完成后,关闭倒计时,移除layer动画。
  5. UIView可以响应事件,但是CALayer不行。可以给背景视图添加手势,然后根据手势的落点,来判断是第几个红包。

所有代码

    //
//  ViewController.m
//  mooncake
//
//  Created by  on 2021/9/17.
//

#import "ViewController.h"
#import <Masonry/Masonry.h>

@interface ViewController ()

@property (nonatomic, strong) UILabel *countdownLabel;
@property (nonatomic, strong) CALayer *animationLayer;
@property (nonatomic, strong) UIView *touchView;
@property (nonatomic, strong) NSTimer *timer;

@end

@implementation ViewController

- (UIView *)touchView{
    if (_touchView == nil) {
        _touchView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height)];
    
        UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(clickRed:)];
        [_touchView addGestureRecognizer:gesture];
    
    }
    return _touchView;
}
- (UILabel *)countdownLabel
{
    if (_countdownLabel == nil) {
        _countdownLabel = [[UILabel alloc] initWithFrame:CGRectMake(([UIScreen mainScreen].bounds.size.width - 200)*0.5, ([UIScreen mainScreen].bounds.size.height - 200)*0.5, 200, 200)];
        _countdownLabel.font = [UIFont systemFontOfSize:80 weight:UIFontWeightBold];
        _countdownLabel.textAlignment = NSTextAlignmentCenter;
    
    }
    return _countdownLabel;
}

- (void)startTime
{
    __block int timeout = 5;
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,queue);
    dispatch_source_set_timer(_timer,dispatch_walltime(NULL, 0),1.0*NSEC_PER_SEC, 0);
    dispatch_source_set_event_handler(_timer, ^{
        if ( timeout <= 0 )
        {
            dispatch_source_cancel(_timer);
            dispatch_async(dispatch_get_main_queue(), ^{
                [self startRedPackerts];
                self.countdownLabel.hidden = YES;

            });
        }
        else
        {
            NSString * titleStr = [NSString stringWithFormat:@"%d",timeout];
            dispatch_async(dispatch_get_main_queue(), ^{
                self.countdownLabelcountdownLabelcountdownLab.text = titleStr;
            });
            timeout--;
        }
    });
    dispatch_resume(_timer);
}

- (void)startRedPackerts
{
    
    if (self.timer) {
        [self.timer invalidate];
    }
    self.timer= nil;
    
    self.timer = [NSTimer scheduledTimerWithTimeInterval:(1/2.0) target:self selector:@selector(showRain) userInfo:nil repeats:YES];
}

- (void)showRain
{
    UIImageView * imageV = [UIImageView new];
    imageV.image = [UIImage imageNamed:@"red_pocket"];


    imageV.frame = CGRectMake(0, 0, 40 , 52.5 );

    self.animationLayer = [CALayer new];
    self.animationLayer.bounds = imageV.frame;
    self.animationLayer.anchorPoint = CGPointMake(0, 0);
    self.animationLayer.position = CGPointMake(0, -52.5 );
    self.animationLayer.contents = (id)imageV.image.CGImage;
    [self.touchView.layer addSublayer:self.animationLayer];

    [self addAnimation];
}

- (void)addAnimation
{
    CAKeyframeAnimation * moveAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    NSValue * A = [NSValue valueWithCGPoint:CGPointMake(arc4random() % 414, 0)];
    NSValue * B = [NSValue valueWithCGPoint:CGPointMake(arc4random() % 414, [UIScreen mainScreen].bounds.size.height)];
    moveAnimation.values = @[A,B];
    moveAnimation.duration = arc4random() % 200 / 100.0 + 3.5;
    moveAnimation.repeatCount = 1;
    moveAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    [self.animationLayer addAnimation:moveAnimation forKey:nil];

    CAKeyframeAnimation * tranAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
    CATransform3D r0 = CATransform3DMakeRotation(M_PI/180 * (arc4random() % 360 ) , 0, 0, -1);
    CATransform3D r1 = CATransform3DMakeRotation(M_PI/180 * (arc4random() % 360 ) , 0, 0, -1);
    tranAnimation.values = @[[NSValue valueWithCATransform3D:r0],[NSValue valueWithCATransform3D:r1]];
    tranAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    tranAnimation.duration = arc4random() % 200 / 100.0 + 3.5;
    //为了避免旋转动画完成后再次回到初始状态。
    [tranAnimation setFillMode:kCAFillModeForwards];
    [tranAnimation setRemovedOnCompletion:NO];
    [self.animationLayer addAnimation:tranAnimation forKey:nil];
}

- (void)endAnimation
{
    [self.timer invalidate];

    for (NSInteger i = 0; i < self.touchView.layer.sublayers.count ; i ++)
    {
        CALayer * layer = self.touchView.layer.sublayers[i];
        [layer removeAllAnimations];
    }
}

- (void)clickRed:(UITapGestureRecognizer *)sender
{
    CGPoint point = [sender locationInView:self.touchView];
    for (int i = 0 ; i < self.touchView.layer.sublayers.count ; i ++)
    {
        CALayer * layer = self.touchView.layer.sublayers[i];
        if ([[layer presentationLayer] hitTest:point] != nil)
        {
            NSLog(@"%d",i);
        
            BOOL hasRedPacketd = !(i % 2) ;
        
            UIImageView * newPacketIV = [UIImageView new];
            if (hasRedPacketd)
            {
                newPacketIV.image = [UIImage imageNamed:@"moon_cake"];
                newPacketIV.backgroundColor = [UIColor redColor];
            
                newPacketIV.frame = CGRectMake(0, 0, 63.5, 74);
            }
            else
            {
                newPacketIV.image = [UIImage imageNamed:@"moon_cry"];
                newPacketIV.backgroundColor = [UIColor greenColor];

                newPacketIV.frame = CGRectMake(0, 0, 45.5, 76.5);
            }
        
            layer.contents = (id)newPacketIV.image.CGImage;
        
            UIView * alertView = [UIView new];
            alertView.layer.cornerRadius = 5;
            alertView.frame = CGRectMake(point.x - 50, point.y, 100, 30);
            [self.touchView addSubview:alertView];
        
            UILabel * label = [UILabel new];
            label.font = [UIFont systemFontOfSize:17];
        
            if (!hasRedPacketd)
            {
                label.text = @"嘤嘤嘤!旺旺旺";
                label.textColor = [UIColor blackColor];
            }
            else
            {
                NSString * string = [NSString stringWithFormat:@"+%d月饼券",i];
                NSString * iString = [NSString stringWithFormat:@"%d",i];
                NSMutableAttributedString * attributedStr = [[NSMutableAttributedString alloc]initWithString:string];
            
                [attributedStr addAttribute:NSFontAttributeName
                                  value:[UIFont systemFontOfSize:27]
                                  range:NSMakeRange(0, 1)];
                [attributedStr addAttribute:NSFontAttributeName
                                  value:[UIFont fontWithName:@"PingFangTC-Semibold" size:32]
                                  range:NSMakeRange(1, iString.length)];
                [attributedStr addAttribute:NSFontAttributeName
                                  value:[UIFont systemFontOfSize:17]
                                  range:NSMakeRange(1 + iString.length, 2)];
                label.attributedText = attributedStr;
                label.textColor = [UIColor blueColor];
            }
        
            [alertView addSubview:label];
            [label mas_makeConstraints:^(MASConstraintMaker *make) {
                make.centerX.equalTo(alertView.mas_centerX);
                make.centerY.equalTo(alertView.mas_centerY);
            }];
        
            [UIView animateWithDuration:1 animations:^{
                alertView.alpha = 0;
                alertView.frame = CGRectMake(point.x- 50, point.y - 100, 100, 30);
            } completion:^(BOOL finished) {
                [alertView removeFromSuperview];
            }];
        }
    }
}

- (void)viewDidLoad {
    [super viewDidLoad];
    [self.view addSubview:self.countdownLabel];
    [self.view addSubview:self.touchView];
    [self startTime];
}


@end