iOS抽奖转盘下篇:转盘主视图的实现(内含完整Demo)

网友投稿 703 2022-09-12

iOS抽奖转盘下篇:转盘主视图的实现(内含完整Demo)

iOS抽奖转盘下篇:转盘主视图的实现(内含完整Demo)

小知识,大挑战!本文正在参与“​​程序员必备小知识​​”创作活动。

引言

原理:利用CoreGraphics进行自定义转盘的绘制

I 、概率抽奖算法 & 转盘算法

II 、转盘主视图的实现

2.1 子视图

属性

初始化转盘视图

- (instancetype)initWithFrame:(CGRect)frame { return [self initWithFrame:frame ViewModel:nil];}- (instancetype)initWithCoder:(NSCoder *)aDecoder { return [self initWithFrame:CGRectZero ViewModel:nil];}- (instancetype)initWithViewModel:(id)ViewModel { return [self initWithFrame:CGRectZero ViewModel:ViewModel];}- (instancetype)initWithFrame:(CGRect)frame ViewModel:(id)viewModel { if (self = [super initWithFrame:frame]) { _viewModel = viewModel; [self selfInit]; [self createSubView]; [self setupdata]; [self bindViewModel]; } return self;}- (void)selfInit{// self.backgroundColor = k_view_backColor;}- (void)createSubView{ [self turntable]; self.turntable.luckyItemArray = _viewModel.luckyItemArray; [self initStartBtn];}- (void)setupdata{}- (void)bindViewModel{}- (SubTurntableView *)turntable{ if(_turntable == nil){ _turntable = [[SubTurntableView alloc] init]; [self addSubview:_turntable]; __weak __typeof__(self) weakSelf = self; [_turntable mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(weakSelf); }]; [_turntable setRotaryEndTurnBlock:^{ [weakSelf lunckyAnimationDidStop]; }]; } return _turntable;;}

1、判断用户是否可以抽奖

禁用按钮 self.startButton.enabled = NO;

2、发起网络请求获取当前选中奖品,demo通过随机的方式获取一次index; 另外一种是根据奖品百分比进行控制

3、拿到当前奖品的 找到其对于的位置

4、让转盘转起来

/** //1、判断用户是否可以抽奖 //禁用按钮 // self.startButton.enabled = NO; //2、发起网络请求获取当前选中奖品,demo通过随机的方式获取一次index; 另外一种是根据奖品百分比进行控制 //3、拿到当前奖品的 找到其对于的位置 //4、让转盘转起来 */- (void)startAction { [self setupStartBtnState4noenable]; // 方式一: 发起网络请求获取当前选中奖品,demo通过随机的方式获取一次index;// self.viewModel.endId = arc4random() % self.viewModel.luckyItemArray.count; // 控制中奖的方式二:另外一种是根据奖品百分比进行控制中奖概率// NSInteger randomNum = arc4random()%100;//控制概率// 奖品 title A ,index下标0,中奖 概率probability80%, 就是当randomNum为0-80,返回中奖下标0 // 为了便于理解,我们称奖品A的【随机中奖范围】 probabilityRange为0-80 // // 根据randomNum,确定中奖奖品 KNTurntableViewModel *tmp = [KNTurntableViewModel getMbyprobabilityRangeWithArr:self.viewModel.luckyItemArray]; // return nil;// 谢谢参与 if(tmp){ self.viewModel.endId = tmp.index; }else{ self.viewModel.endId = 0;// 谢谢参与 } [self turntableRotate:self.viewModel.endId];}

2.3 抽奖结束,弹出奖品

III、绘制转盘

原理:利用CoreGraphics进行自定义转盘的绘制

头文件

#import "KNTurntableViewModel.h"#import #define D2R(degrees) ((M_PI * degrees) / 180)@interface SubTurntableView : UIView/** 奖品数据 */@property (nonatomic, strong) NSArray *luckyItemArray;-(void)animationWithSelectonIndex:(NSInteger)index;//结束旋转@property (nonatomic, copy) void (^rotaryEndTurnBlock)(void);

根据奖品绘制转盘

#import #import "SubTurntableView.h"#import "UIImage+CGImageRef.h"@import CoreGraphics;@interface SubTurntableView (){ UIFont *_textFont; CGFloat _textFontSize; UIColor *_textFontColor; NSDictionary *_attributes; CGSize _imageSize; //相间颜色 UIColor *_colorA; UIColor *_colorB; UIColor *_circleBgColor;//外环 bgColor UIColor *_dotColor; UIColor *_dotShinningColor; CGFloat _circleWidth; NSInteger _numberOfDot;//default is 18 dots CGFloat _dotSize; // default is 10.0}@property (strong, nonatomic) NSMutableArray *dotLayers;//count = 18@property (strong, nonatomic) NSMutableArray *imageLayers;@property (strong, nonatomic) NSOperationQueue *imageRenderQueue;@property (nonatomic, assign) CGFloat startValue;//default = 0@endstatic CGPoint pointAroundCircumference(CGPoint center, CGFloat radius, CGFloat theta);@implementation SubTurntableView#pragma mark - Draw Method- (void)drawRect:(CGRect)rect{ [super drawRect:rect]; if (_luckyItemArray && _luckyItemArray.count) { [_imageLayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)]; [_imageLayers removeAllObjects]; NSInteger count = _luckyItemArray.count; CGPoint center = CGPointMake(rect.size.width / 2.0, rect.size.height / 2.0); CGFloat degree = 360.0 / count; //draw cicle UIBezierPath *outerPath = [UIBezierPath bezierPathWithArcCenter:center radius:center.x startAngle:0 endAngle:M_PI * 2 clockwise:YES]; UIBezierPath *innerPath = [UIBezierPath bezierPathWithArcCenter:center radius:center.x - _circleWidth startAngle:0 endAngle:M_PI * 2 clockwise:YES]; [outerPath appendPath:innerPath]; [_circleBgColor setFill]; [outerPath fill]; //draw dots 画点 [self drawDotOnCircle]; for (int i = 0; i < count; i++) { KNTurntableViewModel *obj = [_luckyItemArray objectAtIndex:i]; UIBezierPath *fanPath = [UIBezierPath bezierPath];//reference path [fanPath moveToPoint:center]; [fanPath addArcWithCenter:center radius:center.x - _circleWidth startAngle:D2R(i * degree) endAngle:D2R((i + 1) * degree) clockwise:YES]; [fanPath closePath]; if (i%2) { [_colorA setFill]; [fanPath fill]; }else{ [_colorB setFill]; [fanPath fill]; } //text 文字 [self drawCurvedStringOnLayer:self.layer withAttributedText:[[NSAttributedString alloc] initWithString:obj.title attributes:_attributes] atAngle:D2R((i + 0.5) * degree) withRadius:center.x - _circleWidth - _textFontSize - 2]; //image 图片 CALayer *imageLayer = [CALayer layer]; NSBlockOperation *operaton = [NSBlockOperation blockOperationWithBlock:^{ [[NSOperationQueue mainQueue] addOperationWithBlock:^{ UIImage *image = [UIImage imageNamed:obj.imageName]; CGImageRef imageRef = [image newCGImageRenderedInBitmapContext]; imageLayer.contents = (__bridge id)imageRef; }]; }]; [self.imageRenderQueue addOperation:operaton]; // VKOOY CGPoint imageLayerPos = pointAroundCircumference(center, (center.x - _circleWidth) / 2.0, D2R((i + 0.5) * degree)); imageLayer.frame = CGRectMake(0, 0, _imageSize.width, _imageSize.height); imageLayer.position = imageLayerPos; imageLayer.affineTransform = CGAffineTransformRotate(CGAffineTransformIdentity, D2R((i + 0.5) * degree) + M_PI_2); imageLayer.cornerRadius = 3.0; imageLayer.masksToBounds = YES; [self.layer addSublayer:imageLayer]; [self.imageLayers addObject:imageLayer]; } } [self RotationWithEndValue: @(0 - M_PI/2) duration:0.001 delegate:nil];}/** 画点*/- (void)drawDotOnCircle{ [_dotLayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)]; [_dotLayers makeObjectsPerformSelector:@selector(removeAllAnimations)]; [_dotLayers removeAllObjects]; CGPoint center = CGPointMake(self.bounds.size.width / 2.0, self.bounds.size.height / 2.0); CGFloat dotRadians = M_PI*2 / _numberOfDot; CABasicAnimation *shinningAnimation = [CABasicAnimation animationWithKeyPath:@"backgroundColor"]; for (int i = 0; i < _numberOfDot; i++) { CAShapeLayer *dotLayer = [CAShapeLayer layer]; dotLayer.frame = CGRectMake(0, 0, _dotSize, _dotSize); dotLayer.cornerRadius = _dotSize / 2.0; dotLayer.position = pointAroundCircumference(center, center.x - _circleWidth / 2.0, i * dotRadians); dotLayer.backgroundColor = (i % 2) ? _dotColor.CGColor : _dotShinningColor.CGColor; [_dotLayers addObject:dotLayer]; [self.layer addSublayer:dotLayer]; shinningAnimation.fromValue = (id)(dotLayer.backgroundColor); shinningAnimation.toValue = (id)((i % 2) ? _dotShinningColor.CGColor : _dotColor.CGColor); shinningAnimation.duration = 0.25f; shinningAnimation.repeatCount = 1000; shinningAnimation.autoreverses = YES; [dotLayer addAnimation:shinningAnimation forKey:@"backgroundColor"]; }}- (void)addAnimation2DotLayer{ for (int i = 0; i < _numberOfDot; i++) { CAShapeLayer *dotLayer = [_dotLayers objectAtIndex:i]; [dotLayer removeAllAnimations]; CABasicAnimation *shinningAnimation = [CABasicAnimation animationWithKeyPath:@"backgroundColor"]; shinningAnimation.fromValue = (id)(dotLayer.backgroundColor); shinningAnimation.toValue = (id)((i % 2) ? _dotShinningColor.CGColor : _dotColor.CGColor); shinningAnimation.duration = 0.25f; shinningAnimation.repeatCount = 1000; shinningAnimation.autoreverses = YES; [dotLayer addAnimation:shinningAnimation forKey:@"backgroundColor"]; }}// draw fan shaped text(sector text) 画扇形字- (void)drawCurvedStringOnLayer:(CALayer *)layer withAttributedText:(NSAttributedString *)text atAngle:(float)angle withRadius:(float)radius { CGSize textSize = CGRectIntegral([text boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) context:nil]).size; CGFloat perimeter = 2 * M_PI * radius; CGFloat textAngle = (textSize.width / perimeter * 2 * M_PI); CGFloat textRotation = 0; CGFloat textDirection = 0;// if (angle > D2R(10) && angle < D2R(170)) {// 反向 使文字 可读// //bottom string// textRotation = 0.5 * M_PI ;// textDirection = - 2 * M_PI;// angle += textAngle / 2;// } else { //top string textRotation = 1.5 * M_PI ; textDirection = 2 * M_PI; angle -= textAngle / 2;// } for (int c = 0; c < text.length; c++) { NSRange range = {c, 1}; NSAttributedString* letter = [text attributedSubstringFromRange:range]; CGSize charSize = CGRectIntegral([letter boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) context:nil]).size; CGFloat letterAngle = ((charSize.width / perimeter) * textDirection ); CGFloat x = radius * cos(angle + (letterAngle/2)); CGFloat y = radius * sin(angle + (letterAngle/2)); CATextLayer *singleChar = [self drawTextOnLayer:layer withText:letter frame:CGRectMake(layer.frame.size.width/2 - charSize.width/2 + x, layer.frame.size.height/2 - charSize.height/2 + y, charSize.width, charSize.height) bgColor:nil opacity:1]; singleChar.transform = CATransform3DMakeAffineTransform( CGAffineTransformMakeRotation(angle - textRotation) ); angle += letterAngle; }}- (CATextLayer *)drawTextOnLayer:(CALayer *)layer withText:(NSAttributedString *)text frame:(CGRect)frame bgColor:(UIColor *)bgColor opacity:(CGFloat)opacity { CATextLayer *textLayer = [[CATextLayer alloc] init]; [textLayer setFrame:frame]; [textLayer setString:text]; [textLayer setAlignmentMode:kCAAlignmentCenter]; [textLayer setBackgroundColor:bgColor.CGColor]; [textLayer setContentsScale:[UIScreen mainScreen].scale]; [textLayer setOpacity:opacity]; [layer addSublayer:textLayer]; return textLayer;}@end// center point on circle 在圆上的点static CGPoint pointAroundCircumference(CGPoint center, CGFloat radius, CGFloat theta){ CGPoint point = CGPointZero; point.x = center.x + radius * cos(theta); point.y = center.y + radius * sin(theta); return point;}- (void)dealloc{ [_imageRenderQueue cancelAllOperations]; _imageRenderQueue = nil; [_imageLayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)]; [_imageLayers removeAllObjects]; [_dotLayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)]; [_dotLayers removeAllObjects];}#pragma mark - Init Methods- (instancetype)initWithCoder:(NSCoder *)aDecoder{ self = [super initWithCoder:aDecoder]; if (self) { [self defaultSetups]; } return self;}- (instancetype)initWithFrame:(CGRect)frame{ self = [super initWithFrame:frame]; if (self) { [self defaultSetups]; } return self;}#pragma mark - Preparations- (void)defaultSetups{ self.backgroundColor = [UIColor clearColor]; _dotLayers = [NSMutableArray arrayWithCapacity:18]; _textFontSize = 12.0; _textFont = [UIFont systemFontOfSize:_textFontSize]; _textFontColor = [UIColor blackColor]; _attributes = @{ NSForegroundColorAttributeName:_textFontColor, NSFontAttributeName:_textFont }; _imageSize = CGSizeMake(25, 25); _circleWidth = 20.0; _numberOfDot = 18; _dotSize = 8.0; _colorA = [UIColor colorWithRed:249 / 255.0 green:105 / 255.0 blue:108 / 255.0 alpha:1.0]; _colorB = [UIColor colorWithRed:247 / 255.0 green:131 / 255.0 blue:131 / 255.0 alpha:1.0]; _circleBgColor = [UIColor colorWithRed:251 / 255.0 green:94 / 255.0 blue:97 / 255.0 alpha:1.0]; _dotShinningColor = [UIColor colorWithRed:42 / 255.0 green:253 / 255.0 blue:47 / 255.0 alpha:1.0]; _dotColor = [UIColor whiteColor];}#pragma mark - Getter & Setter- (NSOperationQueue *)imageRenderQueue{ if (!_imageRenderQueue) { _imageRenderQueue = [[NSOperationQueue alloc] init]; _imageRenderQueue.name = @""; } return _imageRenderQueue;}- (void)setLuckyItemArray:(NSArray *)luckyItemArray{ _luckyItemArray = luckyItemArray; _numberOfDot = _luckyItemArray.count * 2; [self setNeedsDisplay];}#pragma mark - Public Methods/** 转盘算法 */- (void)animationWithSelectonIndex:(NSInteger)index{ [self backToStartPosition]; double perSection = M_PI*2/_luckyItemArray.count; // //先转4圈 再选区 顺时针(所有这里需要用360-对应的角度) 逆时针不需要 double toValue= ((M_PI*2 - (perSection*index +perSection*0.5)) + M_PI*2*4); [self RotationWithEndValue: @(toValue - M_PI/2) duration:4 delegate:self];// 因为drawRect从正3点开始画,因此- M_PI/2}- (void)RotationWithEndValue:(id)toValue duration:(CFTimeInterval)duration delegate:(id)delegate{ CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"]; animation.toValue = toValue;// animation.duration = duration; //由快变慢 animation.removedOnCompletion = NO; animation.fillMode = kCAFillModeForwards; animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];// animation.delegate = delegate; [self.layer addAnimation:animation forKey:@"rotation"];}-(void)backToStartPosition{ CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"]; animation.toValue = @(0); animation.duration = 0.001; animation.removedOnCompletion = NO; animation.fillMode = kCAFillModeForwards; [self.layer addAnimation:animation forKey:@"rotation"];}#pragma mark - CAAnimationDelegate- (void)animationDidStart:(CAAnimation *)anim{}- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{ if (self.rotaryEndTurnBlock) { self.rotaryEndTurnBlock(); }}

IV、完整Demo-

​​iOS抽奖转盘:概率抽奖算法 & 转盘算法 & 转盘主视图的实现思路

原理:利用CoreGraphics进行自定义转盘的绘制

see also

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:计算机程序的构造和解释 - 个人笔记(一)
下一篇:python基础(16):内置函数(二)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~