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:
- The countdown is implemented with NSTimer.
- The e animation of red envelope rain is realized by CALayer.
- Animation is divided into two parts, the first part is the displacement animation, the second part is the rotation animation.
- After the drop completes, close the countdown and remove the Layer animation.
- 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