I am participating in the Mid-Autumn Festival Creative Submission contest, please see: Mid-Autumn Festival Creative Submission Contest for details

Effect:


Ideas:

1. Generate a red envelope 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];Copy the code

2. Add layer animation

- (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.duration = arc4random() % 200/100.0 + 3.5; // To avoid returning to the initial state after the rotation animation is complete. [tranAnimation setFillMode:kCAFillModeForwards]; [tranAnimation setRemovedOnCompletion:NO]; [self.animationLayer addAnimation:tranAnimation forKey:nil]; }Copy the code

3. Generate red packets regularly

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

4. Judge the red envelope click event

- (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 = @" linkage! Want want;" 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]; }]; }}}Copy the code

5. Stop the red envelope rain

- (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]; }}Copy the code

Summary, matters needing attention:

  1. The countdown is implemented with NSTimer.
  2. The e animation of red envelope rain is realized by CALayer.
  3. Animation is divided into two parts, the first part is the displacement animation, the second part is the rotation animation.
  4. After the drop completes, close the countdown and remove the Layer animation.
  5. UIView can respond to events, but CALayer can’t. You can add a gesture to the background view and determine the number of red packets based on where the gesture falls.

All of the code

    //
//  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
Copy the code