[iOS UI进阶 - 2.4] 彩票Demo v1.4 转盘动画

A.需求
  • 幸运广场界面中有一个幸运转盘,平时能够自动缓缓转动
  • 能够选择星座
  • 点击“开始选号”开速旋转转盘,旋转一定周数
  • 转盘转动速度节奏:开始-慢-块-慢-结束
  • 设置其余的背景和按钮
 
code source:
 
 
HelloLotteryLucky
 
B.实现
1.使用xib设计转盘
Image(23)
 
2.自定义类
(1)自定义一个继承UIVIew的类来控制这个转盘
(2)自定义一个继承UIButton的类来表示12星座按钮
Image(24)
 
3.给转盘添加12星座按钮
(1)先添加12个按钮重叠在12点方向
在awakeFromNib中添加子控件
 1 - (void)awakeFromNib {
 2     // 由于按钮是要作为“转盘”控件的子控件,所以为了能够传递点击事件,要先开启“转盘”(UIImageView)的交互功能
 3     self.luckyWheel.userInteractionEnabled = YES;
 4    
 5     // 添加12个星座按钮
 6     for (int i=0; i<12; i++) {
 7        
 8         // 添加按钮
 9         HVWLuckyWheelButton *button = [[HVWLuckyWheelButton alloc] init];
10         button.backgroundColor = [UIColor redColor];
11        
12         // 设置按钮尺寸
13         button.bounds = CGRectMake(0, 0, 68, 143);
14         // 设置按钮锚点,用于分布环形12个按钮
15         button.layer.anchorPoint = CGPointMake(0.5, 1);
16         // 暂时把所有按钮都放在12点位置
17         button.layer.position = CGPointMake(self.center.x, self.center.y);
18        
19         [self.luckyWheel addSubview:button];
20     }
21 }
 
Image(25)
 
(2)因为设置好了锚点,让12星座按钮旋转,平均排列在转盘上
1         // 环形分布12个星座按钮,围绕着锚点旋转
2         button.transform = CGAffineTransformMakeRotation(M_PI/6 * i);
 
Image(26)
 
(3)裁剪星座图片
星座图片原图
Image(27)
 
注意裁剪图片用的CG框架方法使用的单位是px像素,而UIImage用的是pt点(可以自动识别@2x),所以要根据屏幕分辨率来判别星座小图的裁剪大小。
 1 - (void)awakeFromNib {
 2     // 由于按钮是要作为“转盘”控件的子控件,所以为了能够传递点击事件,要先开启“转盘”(UIImageView)的交互功能
 3     self.luckyWheel.userInteractionEnabled = YES;
 4    
 5     // 添加12个星座按钮
 6     for (int i=0; i<12; i++) {
 7        
 8         // 添加按钮
 9         HVWLuckyWheelButton *button = [[HVWLuckyWheelButton alloc] init];
10         button.backgroundColor = [UIColor redColor];
11        
12         // 设置按钮尺寸
13         button.bounds = CGRectMake(0, 0, 68, 143);
14         // 设置按钮锚点,用于分布环形12个按钮
15         button.layer.anchorPoint = CGPointMake(0.5, 1);
16         // 暂时把所有按钮都放在12点位置
17         button.layer.position = CGPointMake(self.center.x, self.center.y);
18        
19        
20         // 取得星座图片总图
21         UIImage *sumImage = [UIImage imageNamed:@"LuckyAstrology"];
22         UIImage *sumImageSelected = [UIImage imageNamed:@"LuckyAstrologyPressed"];
23        
24         // 使用CoreGraphic分割图片,得到独立的星座小图
25         // 这里图片的大小是提供给CGImageCreateWithImageInRect裁剪图片用的,所以要把单位从pt转换成px
26         // 根据屏幕的分辨率来判别系统是否使用了@2x图片
27         CGFloat conImageWidth = sumImage.size.width / 12 * [UIScreen mainScreen].scale;
28         CGFloat conImageHeight = sumImage.size.height * [UIScreen mainScreen].scale;
29         CGFloat conImageX = i * conImageWidth;
30         CGFloat conImageY = 0;
31        
32         // CGImage是用像素px来测量的,不是点pt,所以不能自动识别普通图片和@2x图片
33         UIImage *conImage = [UIImage imageWithCGImage:CGImageCreateWithImageInRect(sumImage.CGImage, CGRectMake(conImageX, conImageY, conImageWidth, conImageHeight))];
34        
35         UIImage *conImageSelected = [UIImage imageWithCGImage:CGImageCreateWithImageInRect(sumImageSelected.CGImage, CGRectMake(conImageX, conImageY, conImageWidth, conImageHeight))];
36        
37         // 设置按钮图片
38         [button setImage:conImage forState:UIControlStateNormal];
39         [button setImage:conImageSelected forState:UIControlStateSelected];
40         [button setBackgroundImage:[UIImage imageNamed:@"LuckyRototeSelected"] forState:UIControlStateSelected];
41 
42         // 按钮点击事件
43         [button addTarget:self action:@selector(conButtonClicked:) forControlEvents:UIControlEventTouchDown];
44        
45         // 环形分布12个星座按钮,围绕着锚点旋转
46         button.transform = CGAffineTransformMakeRotation(M_PI/6 * i);
47 
48         // 默认选中第一个按钮
49         if (0 == i) {
50             [self conButtonClicked:button];
51         }
52        
53         [self.luckyWheel addSubview:button];
54     }
55 }
56 
57 /** 星座按钮点击事件 */
58 - (void) conButtonClicked:(UIButton *) button {
59     self.selectedLuckyWheelButton.selected = NO;
60     button.selected = YES;
61     self.selectedLuckyWheelButton = button;
62 }
 
Image(28)
 
(4)裁剪出来的图片太大了,在自定义星座按钮类中重新初始化按钮图片
 1 //
 2 //  HVWLuckyWheelButton.m
 3 //  LotteryLuckyWheel
 4 //
 5 //  Created by hellovoidworld on 15/1/16.
 6 //  Copyright (c) 2015年 hellovoidworld. All rights reserved.
 7 //
 8 
 9 #import "HVWLuckyWheelButton.h"
10 
11 @implementation HVWLuckyWheelButton
12 
13 /** 按钮前景图image的位置尺寸初始化 */
14 - (CGRect)imageRectForContentRect:(CGRect)contentRect {
15     CGFloat width = 40;
16     CGFloat height = 47;
17    
18     // 这里一定要用contentRect来取得按钮的尺寸,不能使用self.frame
19     CGFloat x = (contentRect.size.width - width)/2;
20    
21     CGFloat y = 30;
22     return CGRectMake(x, y, width, height);
23 }
24 
25 /** 取消选中效果 */
26 - (void)setHighlighted:(BOOL)highlighted {
27     // 置空
28 }
29 
30 @end
 
Image(29)
 
 
4.让转盘转动起来
 1 /** 开始转盘旋转 */
 2 - (void) startRotate {
 3     if (self.displayLink) return;
 4    
 5     // 创建循环刷新事件
 6     CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(luckyWheelRotate)];
 7     self.displayLink = link;
 8    
 9     // 加入到主循环
10     [link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
11 }
12  
13 /** 转盘旋转实现方法 */
14 - (void) luckyWheelRotate {
15     self.luckyWheel.transform = CGAffineTransformRotate(self.luckyWheel.transform, M_PI_4 / 100);
16 }
17  
18 /** 停止转盘旋转 */
19 - (void) stopRotate {
20     [self.displayLink invalidate];
21     self.displayLink = nil;
22 }
 
LuckyWheelRotate
 
5.“开始选号”按钮
转盘加速旋转
Image(30)
 
 
 1 /**  开始选号 
 2 * 因为点击“开始选号”之后仅播放动画,不用设计交互操作,所以可以用CALayer动画来完成
 3 */
 4 - (IBAction)startChoose {
 5     // 禁止再次点击
 6     self.startChooseButton.userInteractionEnabled = NO;
 7    
 8     // 先停止原来的轮盘转动
 9     [self stopRotate];
10 
11     // 停止轮盘的交互功能
12     self.luckyWheel.userInteractionEnabled = NO;
13    
14     CABasicAnimation *anim = [[CABasicAnimation alloc] init];
15     anim.keyPath = @"transform.rotation";
16 
17     // 使用反余弦函数和矩阵换算计算出转盘现在的旋转角度
18     CGFloat wheelRoate = acosf(self.luckyWheel.transform.a);
19     if (self.luckyWheel.transform.b < 0) {    // 超出180°的情况
20         wheelRoate = M_PI * 2 - wheelRoate;
21     }
22    
23     // 狂转3周,回到原来的位置
24     anim.toValue = @(wheelRoate + M_PI * 2 * 3);
25     anim.duration = 2.0;
26    
27     // 设置代理
28     anim.delegate = self;
29    
30     //使用慢进慢出的动画节奏
31     anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
32    
33     [self.luckyWheel.layer addAnimation:anim forKey:nil];
34 }
35 
36 /**  CALayer动画停止之后 */
37 - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
38     // 恢复轮盘的交互功能
39     self.luckyWheel.userInteractionEnabled = YES;
40    
41     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
42         // 稍后恢复轮盘自动转动
43         [self startRotate];
44        
45         // 恢复“开始选号”按钮点击
46         self.startChooseButton.userInteractionEnabled = YES;
47     });
48 }
 
LuckyWheelStartChoose
 
 
6.放到彩票Demo中,添加上背景和按钮
 1 //
 2 //  HVWLuckyViewController.m
 3 //  HelloLottery
 4 //
 5 //  Created by hellovoidworld on 15/1/16.
 6 //  Copyright (c) 2015年 hellovoidworld. All rights reserved.
 7 //
 8 
 9 #import "HVWLuckyViewController.h"
10 #import "HVWLuckyWheel.h"
11 
12 @interface HVWLuckyViewController ()
13 
14 @end
15 
16 @implementation HVWLuckyViewController
17 
18 - (void)viewDidLoad {
19     [super viewDidLoad];
20     // Do any additional setup after loading the view.
21    
22     [self setEdgesForExtendedLayout:UIRectEdgeNone];
23    
24     // 加载背景
25     // 由于是jpg格式,不能使用imageNamed在iameges.xcassets中寻找
26     NSString *imagePath = [[NSBundle mainBundle] pathForResource:@"LuckyBackground" ofType:@"jpg"];
27     [self.view setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageWithContentsOfFile:imagePath]]];
28    
29     // 加载转盘
30     HVWLuckyWheel *luckyWheel = [HVWLuckyWheel hvwLuckyWheel];
31     luckyWheel.center = CGPointMake(self.view.frame.size.width/2, 220);
32     [luckyWheel startRotate];
33    
34     [self.view addSubview:luckyWheel];
35 }
36 
37 - (void)didReceiveMemoryWarning {
38     [super didReceiveMemoryWarning];
39     // Dispose of any resources that can be recreated.
40 }
41 
42 /*
43 #pragma mark - Navigation
44 
45 // In a storyboard-based application, you will often want to do a little preparation before navigation
46 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
47     // Get the new view controller using [segue destinationViewController].
48     // Pass the selected object to the new view controller.
49 }
50 */
51 
52 @end
 
Image(31)
 
注意:此画面取消了view扩展,所以view的大小就是中间可见部分,不含导航栏和状态栏。
Image(32)
 
 
 
posted @ 2015-01-17 01:04  HelloVoidWorld  阅读(1072)  评论(0编辑  收藏  举报