460 lines
18 KiB
Objective-C
460 lines
18 KiB
Objective-C
//
|
||
// QXSkyPraizeView.m
|
||
// QXLive
|
||
//
|
||
// Created by 启星 on 2025/8/16.
|
||
//
|
||
|
||
#import "QXSkyPraizeView.h"
|
||
/// 共有12个礼物
|
||
static NSInteger giftMaxCount = 12;
|
||
/// 最少转2圈
|
||
static NSInteger minRoundCount = 2;
|
||
/// 距离4个的时候放慢
|
||
static NSInteger toSlowCount = 4;
|
||
|
||
@interface QXSkyPraizeView()<UIGestureRecognizerDelegate>
|
||
/// 目标下标
|
||
@property (nonatomic,assign)NSInteger targetIndex;
|
||
|
||
@property (nonatomic,strong)NSMutableArray* targetArrayIndex;
|
||
@property (nonatomic,strong)NSMutableArray* finishTargetArrayIndex;
|
||
/// 当前转动到第几个下标
|
||
@property (nonatomic,assign)NSInteger currentIndex;
|
||
/// 转动了多少次
|
||
@property (nonatomic,assign)NSInteger roundCount;
|
||
/// 延时
|
||
@property (nonatomic,assign)double delayTime;
|
||
/// 22 抽一次 33 抽十次 44 抽 100次
|
||
@property (nonatomic,assign)NSInteger startType;
|
||
|
||
@property (nonatomic,strong)QXSkyPraizeSubView *currentGiftView;
|
||
|
||
@property (nonatomic,strong)UIView *bgView;
|
||
@property (nonatomic,strong)NSMutableArray *allViewsArray;
|
||
|
||
@property (nonatomic,strong)dispatch_source_t fastTimer;
|
||
@property (nonatomic,strong)dispatch_source_t slowTimer;
|
||
@end
|
||
|
||
@implementation QXSkyPraizeView
|
||
|
||
- (instancetype)init
|
||
{
|
||
self = [super init];
|
||
if (self) {
|
||
self.frame = CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||
[self initSubviews];
|
||
}
|
||
return self;
|
||
}
|
||
-(void)initSubviews{
|
||
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(hide)];
|
||
tap.delegate = self;
|
||
[self addGestureRecognizer:tap];
|
||
self.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.3];
|
||
|
||
self.bgView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, ScaleWidth(520)+kSafeAreaBottom)];
|
||
self.bgView.backgroundColor = UIColor.whiteColor;
|
||
[self.bgView addRoundedCornersWithRadius:16 byRoundingCorners:(UIRectCornerTopLeft|UIRectCornerTopRight)];
|
||
[self addSubview:self.bgView];
|
||
|
||
|
||
CGFloat topMargin = 60;
|
||
CGFloat margin = 16;
|
||
CGFloat space = 10;
|
||
CGFloat itemWidth = (SCREEN_WIDTH - margin*2 - space*3) / 4;
|
||
CGFloat itemHeight = itemWidth/7*8;;
|
||
for (int i = 0; i < giftMaxCount; i++) {
|
||
QXSkyPraizeSubView *giftView;
|
||
if (i < 4) {
|
||
/// 第一行4个
|
||
giftView = [[QXSkyPraizeSubView alloc] initWithFrame:CGRectMake(margin+(itemWidth+space)*i, topMargin, itemWidth, itemHeight)];
|
||
giftView.titleLabel.text = [NSString stringWithFormat:@"%d",i];
|
||
[self.allViewsArray replaceObjectAtIndex:i withObject:giftView];
|
||
}else if (i > 3 && i < 6){
|
||
/// 第二行两个
|
||
if (i == 4) {
|
||
giftView = [[QXSkyPraizeSubView alloc] initWithFrame:CGRectMake(margin, topMargin+itemHeight+space, itemWidth, itemHeight)];
|
||
giftView.titleLabel.text = [NSString stringWithFormat:@"%d",11];
|
||
[self.allViewsArray replaceObjectAtIndex:11 withObject:giftView];
|
||
}else{
|
||
giftView = [[QXSkyPraizeSubView alloc] initWithFrame:CGRectMake(margin+(itemWidth+space)*3, topMargin+itemHeight+space, itemWidth, itemHeight)];
|
||
[self.allViewsArray replaceObjectAtIndex:4 withObject:giftView];
|
||
giftView.titleLabel.text = [NSString stringWithFormat:@"%d",4];
|
||
}
|
||
}else if (i > 5 && i < 8){
|
||
/// 第三行两个
|
||
if (i == 6) {
|
||
giftView = [[QXSkyPraizeSubView alloc] initWithFrame:CGRectMake(margin, topMargin+(itemHeight+space)*2, itemWidth, itemHeight)];
|
||
[self.allViewsArray replaceObjectAtIndex:10 withObject:giftView];
|
||
giftView.titleLabel.text = [NSString stringWithFormat:@"%d",10];
|
||
}else{
|
||
giftView = [[QXSkyPraizeSubView alloc] initWithFrame:CGRectMake(margin+(itemWidth+space)*3, topMargin+(itemHeight+space)*2, itemWidth, itemHeight)];
|
||
[self.allViewsArray replaceObjectAtIndex:5 withObject:giftView];
|
||
giftView.titleLabel.text = [NSString stringWithFormat:@"%d",5];
|
||
}
|
||
}else{
|
||
giftView = [[QXSkyPraizeSubView alloc] initWithFrame:CGRectMake(margin+(itemWidth+space)*(i%4), topMargin+(itemHeight+space)*3, itemWidth, itemHeight)];
|
||
[self.allViewsArray replaceObjectAtIndex:11-(i-6) withObject:giftView];
|
||
giftView.titleLabel.text = [NSString stringWithFormat:@"%d",11-(i-6)];
|
||
}
|
||
|
||
[self.bgView addSubview:giftView];
|
||
}
|
||
[self resetViews];
|
||
|
||
|
||
|
||
UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(16, 20, 100, 40)];
|
||
[btn setTitle:@"抽1次" forState:(UIControlStateNormal)];
|
||
[btn setTitleColor:UIColor.blueColor forState:(UIControlStateNormal)];
|
||
btn.tag = 22;
|
||
[btn addTarget:self action:@selector(startAction:) forControlEvents:(UIControlEventTouchUpInside)];
|
||
[self.bgView addSubview:btn];
|
||
|
||
UIButton *tenBtn = [[UIButton alloc] initWithFrame:CGRectMake(116, 20, 100, 40)];
|
||
[tenBtn setTitle:@"抽10次" forState:(UIControlStateNormal)];
|
||
tenBtn.tag = 33;
|
||
[tenBtn setTitleColor:UIColor.blueColor forState:(UIControlStateNormal)];
|
||
[tenBtn addTarget:self action:@selector(startAction:) forControlEvents:(UIControlEventTouchUpInside)];
|
||
[self.bgView addSubview:tenBtn];
|
||
|
||
|
||
UIButton *hundredBtn = [[UIButton alloc] initWithFrame:CGRectMake(216, 20, 100, 40)];
|
||
[hundredBtn setTitle:@"抽100次" forState:(UIControlStateNormal)];
|
||
hundredBtn.tag = 44;
|
||
[hundredBtn setTitleColor:UIColor.blueColor forState:(UIControlStateNormal)];
|
||
[hundredBtn addTarget:self action:@selector(startAction:) forControlEvents:(UIControlEventTouchUpInside)];
|
||
[self.bgView addSubview:hundredBtn];
|
||
}
|
||
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
|
||
return touch.view == self;
|
||
}
|
||
-(void)startAction:(UIButton*)sender{
|
||
|
||
self.startType = sender.tag;
|
||
/// 获取目标礼物
|
||
[self resetViews];
|
||
[self getTargetGift];
|
||
[self startFastAnimate];
|
||
}
|
||
|
||
|
||
|
||
-(void)resetViews{
|
||
self.targetIndex = -1;
|
||
self.currentIndex = -1;
|
||
self.roundCount = 0;
|
||
self.delayTime = 0.3;
|
||
[self.targetArrayIndex removeAllObjects];
|
||
[self.finishTargetArrayIndex removeAllObjects];
|
||
if (self.currentGiftView != nil) {
|
||
self.currentGiftView.isSelected = NO;
|
||
[self.currentGiftView stopPulseAnimationWithLayer];
|
||
}
|
||
for (QXSkyPraizeSubView *giftView in self.allViewsArray) {
|
||
giftView.isSelected = NO;
|
||
[giftView stopPulseAnimationWithLayer];
|
||
}
|
||
}
|
||
|
||
-(void)startFastAnimate{
|
||
self.currentGiftView = self.allViewsArray.firstObject;
|
||
self.currentGiftView.isSelected = YES;
|
||
[self stopFastAnimate];
|
||
if (_fastTimer == nil) {
|
||
_fastTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
|
||
}
|
||
// dispatch_source_set_timer(_fastTimer,
|
||
// dispatch_time(DISPATCH_TIME_NOW, 0),
|
||
// NSEC_PER_SEC * 0.06, // 间隔0.1秒
|
||
// NSEC_PER_SEC * 0.01); // 允许误差0.01秒
|
||
|
||
dispatch_source_set_timer(_fastTimer,
|
||
dispatch_time(DISPATCH_TIME_NOW, 0.06 * NSEC_PER_SEC),
|
||
DISPATCH_TIME_FOREVER, // 使用一次性模式
|
||
0.01 * NSEC_PER_SEC);
|
||
__weak typeof(self) weakSelf = self;
|
||
dispatch_source_set_event_handler(_fastTimer, ^{
|
||
__strong typeof(weakSelf) strongSelf = weakSelf;
|
||
if (!strongSelf) return;
|
||
// 如果需要更新UI,切换到主线程
|
||
dispatch_async(dispatch_get_main_queue(), ^{
|
||
strongSelf.roundCount++;
|
||
|
||
if ((strongSelf.roundCount / giftMaxCount == minRoundCount) && strongSelf.startType != 22) {
|
||
BOOL has = NO;
|
||
for (NSNumber *index in self.finishTargetArrayIndex) {
|
||
if (index.integerValue == self.currentIndex) {
|
||
has = YES;
|
||
break;
|
||
}
|
||
}
|
||
self.currentGiftView.isSelected = NO;
|
||
if (has) {
|
||
QXSkyPraizeSubView *giftView = strongSelf.allViewsArray[strongSelf.currentIndex];
|
||
giftView.isSelected = YES;
|
||
}else{
|
||
QXSkyPraizeSubView *giftView = strongSelf.allViewsArray[strongSelf.currentIndex];
|
||
giftView.isSelected = NO;
|
||
}
|
||
}else{
|
||
/// 把上一个选中状态置为未选中
|
||
strongSelf.currentGiftView.isSelected = NO;
|
||
}
|
||
/// 计算当前下标
|
||
strongSelf.currentIndex = strongSelf.roundCount%giftMaxCount;
|
||
/// 获取当前选中的view 并设置为选中状态
|
||
QXSkyPraizeSubView *giftView = strongSelf.allViewsArray[strongSelf.currentIndex];
|
||
giftView.isSelected = YES;
|
||
|
||
/// 重新赋值给选中view
|
||
strongSelf.currentGiftView = giftView;
|
||
|
||
if (strongSelf.roundCount / giftMaxCount == minRoundCount) {
|
||
|
||
/// 已经转了2圈
|
||
if (strongSelf.startType == 22) {
|
||
|
||
NSInteger index = strongSelf.targetIndex;
|
||
// 0 - > 11
|
||
// 1 - > 12
|
||
// 2->13
|
||
// 3->14
|
||
// 4->15
|
||
// 处理0-4数据无法匹配问题
|
||
if (index < 5) {
|
||
index = (giftMaxCount-1) + index;
|
||
}
|
||
if ((strongSelf.currentIndex + toSlowCount) == index) {
|
||
//距离目标差4
|
||
QXLOG(@"当前下标为%ld---目标为%ld",strongSelf.currentIndex,strongSelf.targetIndex);
|
||
[self stopFastAnimate];
|
||
[self startSlowAnimate];
|
||
}else{
|
||
dispatch_source_set_timer(strongSelf.fastTimer,
|
||
dispatch_time(DISPATCH_TIME_NOW, 0.06 * NSEC_PER_SEC),
|
||
DISPATCH_TIME_FOREVER,
|
||
0.01 * NSEC_PER_SEC);
|
||
}
|
||
|
||
}else{
|
||
// for (QXSkyPraizeSubView *giftView in strongSelf.allViewsArray) {
|
||
// giftView.isSelected = NO;
|
||
// }
|
||
BOOL has = NO;
|
||
for (NSNumber *index in strongSelf.targetArrayIndex) {
|
||
if (strongSelf.currentIndex == index.integerValue) {
|
||
QXSkyPraizeSubView *giftView = strongSelf.allViewsArray[index.integerValue];
|
||
giftView.isSelected = YES;
|
||
[giftView startPulseAnimationWithLayer];
|
||
// [strongSelf pauseGCDTimer];
|
||
[strongSelf.targetArrayIndex removeObject:index];
|
||
[strongSelf.finishTargetArrayIndex addObject:index];
|
||
has = YES;
|
||
break;
|
||
}
|
||
}
|
||
// dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||
// [strongSelf resumeGCDTimer];
|
||
// });
|
||
if (has) {
|
||
dispatch_source_set_timer(strongSelf.fastTimer,
|
||
dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC),
|
||
DISPATCH_TIME_FOREVER,
|
||
0.01 * NSEC_PER_SEC);
|
||
}else{
|
||
dispatch_source_set_timer(strongSelf.fastTimer,
|
||
dispatch_time(DISPATCH_TIME_NOW, 0.06 * NSEC_PER_SEC),
|
||
DISPATCH_TIME_FOREVER,
|
||
0.01 * NSEC_PER_SEC);
|
||
}
|
||
if (strongSelf.targetArrayIndex.count == 0) {
|
||
[strongSelf stopFastAnimate];
|
||
}
|
||
}
|
||
}else{
|
||
dispatch_source_set_timer(strongSelf.fastTimer,
|
||
dispatch_time(DISPATCH_TIME_NOW, 0.06 * NSEC_PER_SEC),
|
||
DISPATCH_TIME_FOREVER,
|
||
0.01 * NSEC_PER_SEC);
|
||
}
|
||
|
||
|
||
});
|
||
});
|
||
dispatch_resume(_fastTimer);
|
||
}
|
||
|
||
-(void)startSlowAnimate{
|
||
|
||
if (_slowTimer == nil) {
|
||
_slowTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
|
||
}
|
||
// 立即开始,初始间隔0.1秒
|
||
dispatch_source_set_timer(_slowTimer,
|
||
dispatch_time(DISPATCH_TIME_NOW, self.delayTime * NSEC_PER_SEC),
|
||
DISPATCH_TIME_FOREVER, // 使用一次性模式
|
||
0.01 * NSEC_PER_SEC);
|
||
__weak typeof(self) weakSelf = self;
|
||
dispatch_source_set_event_handler(_slowTimer, ^{
|
||
__strong typeof(weakSelf) strongSelf = weakSelf;
|
||
if (!strongSelf) return;
|
||
// 如果需要更新UI,切换到主线程
|
||
dispatch_async(dispatch_get_main_queue(), ^{
|
||
|
||
/// 把上一个选中状态置为未选中
|
||
strongSelf.currentGiftView.isSelected = NO;
|
||
|
||
strongSelf.roundCount++;
|
||
/// 计算当前下标
|
||
strongSelf.currentIndex = strongSelf.roundCount%giftMaxCount;
|
||
/// 获取当前选中的view 并设置为选中状态
|
||
QXSkyPraizeSubView *giftView = strongSelf.allViewsArray[strongSelf.currentIndex];
|
||
giftView.isSelected = YES;
|
||
|
||
/// 重新赋值给选中view
|
||
strongSelf.currentGiftView = giftView;
|
||
|
||
if (strongSelf.currentIndex == strongSelf.targetIndex) {
|
||
//距离目标差4
|
||
QXLOG(@"礼物结束====当前下标为%ld---目标为%ld",strongSelf.currentIndex,strongSelf.targetIndex);
|
||
[strongSelf stopSlowAnimate];
|
||
[strongSelf.currentGiftView startPulseAnimationWithLayer];
|
||
}
|
||
});
|
||
strongSelf.delayTime = strongSelf.delayTime+0.1;
|
||
dispatch_source_set_timer(strongSelf.slowTimer,
|
||
dispatch_time(DISPATCH_TIME_NOW, strongSelf.delayTime * NSEC_PER_SEC),
|
||
DISPATCH_TIME_FOREVER,
|
||
0.01 * NSEC_PER_SEC);
|
||
});
|
||
dispatch_resume(_slowTimer);
|
||
}
|
||
|
||
|
||
|
||
-(void)stopFastAnimate{
|
||
if (_fastTimer != nil) {
|
||
dispatch_source_cancel(_fastTimer);
|
||
_fastTimer = nil;
|
||
}
|
||
}
|
||
|
||
- (void)pauseGCDTimer {
|
||
if (_fastTimer) {
|
||
dispatch_suspend(_fastTimer);
|
||
NSLog(@"GCD定时器已暂停");
|
||
}
|
||
}
|
||
- (void)resumeGCDTimer {
|
||
if (_fastTimer) {
|
||
dispatch_resume(_fastTimer);
|
||
NSLog(@"GCD定时器已恢复");
|
||
}
|
||
}
|
||
|
||
-(void)stopSlowAnimate{
|
||
if (_slowTimer != nil) {
|
||
dispatch_source_cancel(_slowTimer);
|
||
_slowTimer = nil;
|
||
}
|
||
}
|
||
|
||
-(void)getTargetGift{
|
||
self.targetArrayIndex = [NSMutableArray arrayWithArray:@[@3,@5,@8,@10]];
|
||
if (self.startType == 22) {
|
||
self.targetIndex = arc4random() % 12;
|
||
}else{
|
||
// NSNumber *number = self.targetArrayIndex.firstObject;
|
||
// self.targetIndex = number.integerValue;
|
||
}
|
||
}
|
||
|
||
-(void)getTenTargetGift{
|
||
|
||
}
|
||
|
||
-(NSMutableArray *)allViewsArray{
|
||
if (!_allViewsArray) {
|
||
_allViewsArray = [NSMutableArray arrayWithArray:@[@"",@"",@"",@"",@"",@"",@"",@"",@"",@"",@"",@""]];
|
||
}
|
||
return _allViewsArray;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
-(NSMutableArray *)finishTargetArrayIndex{
|
||
if (!_finishTargetArrayIndex) {
|
||
_finishTargetArrayIndex = [NSMutableArray array];
|
||
}
|
||
return _finishTargetArrayIndex;
|
||
}
|
||
-(void)showInView:(UIView *)view{
|
||
self.bgView.y = SCREEN_HEIGHT;
|
||
[view addSubview:self];
|
||
[UIView animateWithDuration:0.3 animations:^{
|
||
self.bgView.y = SCREEN_HEIGHT-ScaleWidth(520)-kSafeAreaBottom;
|
||
}];
|
||
}
|
||
-(void)hide{
|
||
[self stopFastAnimate];
|
||
[self stopSlowAnimate];
|
||
[UIView animateWithDuration:0.3 animations:^{
|
||
self.bgView.y = SCREEN_HEIGHT;
|
||
} completion:^(BOOL finished) {
|
||
[self removeFromSuperview];
|
||
}];
|
||
}
|
||
@end
|
||
|
||
|
||
@implementation QXSkyPraizeSubView
|
||
|
||
- (instancetype)initWithFrame:(CGRect)frame
|
||
{
|
||
self = [super initWithFrame:frame];
|
||
if (self) {
|
||
self.backgroundColor = [UIColor cyanColor];
|
||
[self initSubviews];
|
||
}
|
||
return self;
|
||
}
|
||
-(void)initSubviews{
|
||
self.titleLabel = [[UILabel alloc] init];
|
||
self.titleLabel.font = [UIFont boldSystemFontOfSize:22];
|
||
self.titleLabel.textColor = UIColor.blackColor;
|
||
self.titleLabel.textAlignment = NSTextAlignmentCenter;
|
||
[self addSubview:self.titleLabel];
|
||
[self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||
make.edges.equalTo(self);
|
||
}];
|
||
}
|
||
-(void)setIsSelected:(BOOL)isSelected{
|
||
_isSelected = isSelected;
|
||
self.backgroundColor = isSelected?[UIColor blueColor]:[UIColor cyanColor];
|
||
}
|
||
- (void)startPulseAnimationWithLayer {
|
||
CABasicAnimation *pulseAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
|
||
pulseAnimation.duration = 0.5;
|
||
pulseAnimation.fromValue = @0.9;
|
||
pulseAnimation.toValue = @1.1;
|
||
pulseAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
|
||
pulseAnimation.autoreverses = YES;
|
||
pulseAnimation.repeatCount = HUGE_VALF; // 无限循环
|
||
|
||
[self.layer addAnimation:pulseAnimation forKey:@"pulse"];
|
||
|
||
}
|
||
|
||
// 停止动画
|
||
- (void)stopPulseAnimationWithLayer {
|
||
[self.layer removeAnimationForKey:@"pulse"];
|
||
}
|
||
@end
|