一. 星座转盘
1. 示意图
2. 设计思路
每一个星座条是一个UIButton,设置按钮的宽高,设置position点在整个转盘的中点,再通过anchorPoint(0.5, 1)定位到position点
每一个按钮上的图片通过截取图片获取
3. 代码
1> 通过一个xib描述转盘底座,关联到一个新创建的View类(LDWheelView.h),在View中加载xib进行初始化
+ (instancetype)wheel{return [[self alloc] init];}- (instancetype)initWithFrame:(CGRect)frame{if (self = [super initWithFrame:frame]) {self = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([LDWheelView class]) owner:nil options:nil] lastObject];}return self;}- (void)awakeFromNib{// 创建按钮[self setupBtn];}
2> 初始化过程中创建12星座按钮,并设置按钮tag
- (void)setupBtn{// wheelImageV允许和用户互动self.wheelImageV.userInteractionEnabled = YES;CGFloat btnW = 68;CGFloat btnH = 143;CGFloat angle = 0;// 0.加载图片UIImage *imageNor = [UIImage imageNamed:@"LuckyAstrology"];UIImage *imageSel = [UIImage imageNamed:@"LuckyAstrologyPressed"];// 加载图片时只能加载圈1X倍的图片,而屏幕显示时圈2X倍的图片,根据屏幕计算出要倍数CGFloat scale = [UIScreen mainScreen].scale;CGFloat cutImageW = imageNor.size.width / 12 * scale;CGFloat cutImageH = imageNor.size.height *scale;for (int i = 0; i < 12; ++i) {// 1.创建按钮LDWheelBtn *btn = [LDWheelBtn buttonWithType:UIButtonTypeCustom];btn.tag = i;// 2.设置选中背景色[btn setBackgroundImage:[UIImage imageNamed:@"LuckyRototeSelected"] forState:UIControlStateSelected];// 3.设置位置和尺寸btn.bounds = CGRectMake(0, 0, btnW, btnH);btn.layer.anchorPoint = CGPointMake(0.5, 1);btn.layer.position = CGPointMake(self.wheelImageV.bounds.size.width * 0.5, self.wheelImageV.bounds.size.width * 0.5);// 4.旋转角度btn.transform = CGAffineTransformMakeRotation(angle / 180.0 * M_PI);// 每创建一个按钮旋转角度增加30angle += 30;// 5.设置图片CGImageRef imageRefNor = CGImageCreateWithImageInRect(imageNor.CGImage, CGRectMake(i * cutImageW, 0, cutImageW, cutImageH));CGImageRef imageRefSel = CGImageCreateWithImageInRect(imageSel.CGImage, CGRectMake(i * cutImageW, 0, cutImageW, cutImageH));[btn setImage:[UIImage imageWithCGImage:imageRefNor] forState:UIControlStateNormal];[btn setImage:[UIImage imageWithCGImage:imageRefSel] forState:UIControlStateSelected];// 5.添加到wheelImageV上[self.wheelImageV addSubview:btn];// 6.监听按钮[btn addTarget:self action:@selector(setSelect:) forControlEvents:UIControlEventTouchUpInside];if (i == 0) {[self setSelect:btn];}}}
注:扣取一张图片中某个位置为新图片
. 加载图片
UIImage *imageNor = [UIImage imageNamed:@"LuckyAstrology"];
. 加载图片时只能加载圈1X倍的图片,而屏幕显示时圈2X倍的图片,根据屏幕计算出要倍数
CGFloat scale = [UIScreen mainScreen].scale;
. 根据屏幕的倍数x原始图片的宽度/高度 ➗ 12 算出扣取图片的宽高
CGFloat cutImageW = imageNor.size.width / 12 * scale;CGFloat cutImageH = imageNor.size.height *scale;
. 扣取图片 CGImageCreateWithImageInRect 方法
CGImageRef imageRefNor = CGImageCreateWithImageInRect(imageNor.CGImage, CGRectMake(i * cutImageW, 0, cutImageW, cutImageH));
3> 设置选中按钮, 在初始化过程中创建按钮并监听按钮,现在实现监听方法
定义按钮属性记录上一个按钮
/** 记录上一个按钮 */@property (nonatomic, weak) LDWheelBtn *preBtn;
实现监听方法:
. 将上一个按钮selected设置为NO
. 将当前按钮selected设置为YES
. 将当前按钮赋值给上一个按钮
- (void)setSelect:(LDWheelBtn *)btn{// 1.设置上一个按钮selected为NOself.preBtn.selected = NO;// 2.设置当前按钮selected为YESbtn.selected = YES;// 3.将当前按钮赋值给上一个按钮self.preBtn = btn;}
4> 由于点击按钮不需要高亮状态,所以就需要自定义按钮重写设置按钮高亮方法
. 自定义按钮创建一个类(LDWheelBtn.h)继承UIButton
// 取消按钮高亮状态- (void)setHighlighted:(BOOL)highlighted{}
. 重置按钮上图片的位置和尺寸
//返回按钮当中图片的尺寸位置.//contentRect:当前按钮的尺寸位置 .- (CGRect)imageRectForContentRect:(CGRect)contentRect{CGFloat btnW = 40;CGFloat btnH = 50;CGFloat btnX = (contentRect.size.width - btnW) * 0.5;CGFloat btnY = 25;return CGRectMake(btnX, btnY, btnW, btnH);}
. 由于按钮是旋转的就有一些重叠,重叠部分会影响点击,因此要在自定义按钮上拦截事件响应
用一种取巧的方法,让按钮重叠部分不能响应事件,即设置一个区域(未重叠区域)才能响应事件
,重写按钮的hitText方法来判定
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{CGRect rect = CGRectMake(0, 0, self.bounds.size.width, 50);if (CGRectContainsPoint(rect, point)) {return [super hitTest:point withEvent:event];}else{return nil;}}
5> 在控制器中点击开始按钮转盘开始转动,调用WheelView中提供的开始转动方法
. 开始转动将定时器的paused属性设置为NO,设置为YES为暂停动画
//开始旋转- (void)start{self.link.paused = NO;}
. 定义定时器属性
/** 定时器.*/@property (nonatomic ,weak) CADisplayLink *link;
. 懒加载创建定时器(CADisplayLink 这个定时器1s刷新60次)
-(CADisplayLink *)link{if (_link == nil) {//添加定时器.CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)];[link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];_link = link;}return _link;}
. 实现定时器监听方法,将转盘旋转(UIView旋转)
- (void)update{//让转盘开始旋转self.contentView.transform = CGAffineTransformRotate(self.contentView.transform, M_PI / 250.0);}
6> 选择一个星座,点击中间的开始选号,当转盘停止旋转时,星座指针指向正上方
. 点击开始选号,创建基本动画,将转盘旋转N圈
//开始选号- (IBAction)chooseNum:(id)sender {//创建动画对象CABasicAnimation *anim = [CABasicAnimation animation];//设置动画的属性值anim.keyPath = @"transform.rotation";anim.toValue = @(M_PI * 4);anim.delegate = self;anim.duration = 0.5;[self.contentView.layer addAnimation:anim forKey:nil];}
. 实现动画停止后的方法,来将指针回转到正上方
//当动画结束时调用.-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{//动画结束时,选择按钮指向最上方.//动画结束时,让选中的按钮倒着旋转回去.CGAffineTransform transform = self.preBtn.transform;CGFloat angle = atan2(transform.b, transform.a);self.contentView.transform = CGAffineTransformMakeRotation(-angle);}