• iOS开发核心动画之星座幸运转盘


    一. 星座转盘

    1. 示意图

     

    2. 设计思路

        每一个星座条是一个UIButton,设置按钮的宽高,设置position点在整个转盘的中点,再通过anchorPoint(0.5, 1)定位到position点

        每一个按钮上的图片通过截取图片获取


    3. 代码

    1> 通过一个xib描述转盘底座,关联到一个新创建的View类(LDWheelView.h),在View中加载xib进行初始化

    1. + (instancetype)wheel
    2. {
    3. return [[self alloc] init];
    4. }
    5. - (instancetype)initWithFrame:(CGRect)frame
    6. {
    7. if (self = [super initWithFrame:frame]) {
    8. self = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([LDWheelView class]) owner:nil options:nil] lastObject];
    9. }
    10. return self;
    11. }
    12. - (void)awakeFromNib
    13. {
    14. // 创建按钮
    15. [self setupBtn];
    16. }

    2> 初始化过程中创建12星座按钮,并设置按钮tag

    1. - (void)setupBtn
    2. {
    3. // wheelImageV允许和用户互动
    4. self.wheelImageV.userInteractionEnabled = YES;
    5. CGFloat btnW = 68;
    6. CGFloat btnH = 143;
    7. CGFloat angle = 0;
    8. // 0.加载图片
    9. UIImage *imageNor = [UIImage imageNamed:@"LuckyAstrology"];
    10. UIImage *imageSel = [UIImage imageNamed:@"LuckyAstrologyPressed"];
    11. // 加载图片时只能加载圈1X倍的图片,而屏幕显示时圈2X倍的图片,根据屏幕计算出要倍数
    12. CGFloat scale = [UIScreen mainScreen].scale;
    13. CGFloat cutImageW = imageNor.size.width / 12 * scale;
    14. CGFloat cutImageH = imageNor.size.height *scale;
    15. for (int i = 0; i < 12; ++i) {
    16. // 1.创建按钮
    17. LDWheelBtn *btn = [LDWheelBtn buttonWithType:UIButtonTypeCustom];
    18. btn.tag = i;
    19. // 2.设置选中背景色
    20. [btn setBackgroundImage:[UIImage imageNamed:@"LuckyRototeSelected"] forState:UIControlStateSelected];
    21. // 3.设置位置和尺寸
    22. btn.bounds = CGRectMake(0, 0, btnW, btnH);
    23. btn.layer.anchorPoint = CGPointMake(0.5, 1);
    24. btn.layer.position = CGPointMake(self.wheelImageV.bounds.size.width * 0.5, self.wheelImageV.bounds.size.width * 0.5);
    25. // 4.旋转角度
    26. btn.transform = CGAffineTransformMakeRotation(angle / 180.0 * M_PI);
    27. // 每创建一个按钮旋转角度增加30
    28. angle += 30;
    29. // 5.设置图片
    30. CGImageRef imageRefNor = CGImageCreateWithImageInRect(imageNor.CGImage, CGRectMake(i * cutImageW, 0, cutImageW, cutImageH));
    31. CGImageRef imageRefSel = CGImageCreateWithImageInRect(imageSel.CGImage, CGRectMake(i * cutImageW, 0, cutImageW, cutImageH));
    32. [btn setImage:[UIImage imageWithCGImage:imageRefNor] forState:UIControlStateNormal];
    33. [btn setImage:[UIImage imageWithCGImage:imageRefSel] forState:UIControlStateSelected];
    34. // 5.添加到wheelImageV上
    35. [self.wheelImageV addSubview:btn];
    36. // 6.监听按钮
    37. [btn addTarget:self action:@selector(setSelect:) forControlEvents:UIControlEventTouchUpInside];
    38. if (i == 0) {
    39. [self setSelect:btn];
    40. }
    41. }
    42. }

    注:扣取一张图片中某个位置为新图片

    . 加载图片

    1. UIImage *imageNor = [UIImage imageNamed:@"LuckyAstrology"];

    加载图片时只能加载圈1X倍的图片,而屏幕显示时圈2X倍的图片,根据屏幕计算出要倍数

    1. CGFloat scale = [UIScreen mainScreen].scale;

    . 根据屏幕的倍数x原始图片的宽度/高度 ➗ 12 算出扣取图片的宽高

    1. CGFloat cutImageW = imageNor.size.width / 12 * scale;
    2. CGFloat cutImageH = imageNor.size.height *scale;

    . 扣取图片 CGImageCreateWithImageInRect 方法

    1. CGImageRef imageRefNor = CGImageCreateWithImageInRect(imageNor.CGImage, CGRectMake(i * cutImageW, 0, cutImageW, cutImageH));

    3> 设置选中按钮, 在初始化过程中创建按钮并监听按钮,现在实现监听方法

    定义按钮属性记录上一个按钮

    1. /** 记录上一个按钮 */
    2. @property (nonatomic, weak) LDWheelBtn *preBtn;

    实现监听方法:

    . 将上一个按钮selected设置为NO

    . 将当前按钮selected设置为YES

    . 将当前按钮赋值给上一个按钮

    1. - (void)setSelect:(LDWheelBtn *)btn
    2. {
    3. // 1.设置上一个按钮selected为NO
    4. self.preBtn.selected = NO;
    5. // 2.设置当前按钮selected为YES
    6. btn.selected = YES;
    7. // 3.将当前按钮赋值给上一个按钮
    8. self.preBtn = btn;
    9. }

    4> 由于点击按钮不需要高亮状态,所以就需要自定义按钮重写设置按钮高亮方法

    . 自定义按钮创建一个类(LDWheelBtn.h)继承UIButton

    1. // 取消按钮高亮状态
    2. - (void)setHighlighted:(BOOL)highlighted
    3. {
    4. }

    . 重置按钮上图片的位置和尺寸

    1. //返回按钮当中图片的尺寸位置.
    2. //contentRect:当前按钮的尺寸位置 .
    3. - (CGRect)imageRectForContentRect:(CGRect)contentRect{
    4. CGFloat btnW = 40;
    5. CGFloat btnH = 50;
    6. CGFloat btnX = (contentRect.size.width - btnW) * 0.5;
    7. CGFloat btnY = 25;
    8. return CGRectMake(btnX, btnY, btnW, btnH);
    9. }

    . 由于按钮是旋转的就有一些重叠,重叠部分会影响点击,因此要在自定义按钮上拦截事件响应

    用一种取巧的方法,让按钮重叠部分不能响应事件,即设置一个区域(未重叠区域)才能响应事件

    ,重写按钮的hitText方法来判定

    1. -(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    2. CGRect rect = CGRectMake(0, 0, self.bounds.size.width, 50);
    3. if (CGRectContainsPoint(rect, point)) {
    4. return [super hitTest:point withEvent:event];
    5. }else{
    6. return nil;
    7. }
    8. }

    5> 在控制器中点击开始按钮转盘开始转动,调用WheelView中提供的开始转动方法

    . 开始转动将定时器的paused属性设置为NO,设置为YES为暂停动画

    1. //开始旋转
    2. - (void)start
    3. {
    4. self.link.paused = NO;
    5. }

    . 定义定时器属性

    1. /** 定时器.*/
    2. @property (nonatomic ,weak) CADisplayLink *link;

    . 懒加载创建定时器(CADisplayLink 这个定时器1s刷新60次)

    1. -(CADisplayLink *)link{
    2. if (_link == nil) {
    3. //添加定时器.
    4. CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)];
    5. [link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    6. _link = link;
    7. }
    8. return _link;
    9. }

    . 实现定时器监听方法,将转盘旋转(UIView旋转)

    1. - (void)update{
    2. //让转盘开始旋转
    3. self.contentView.transform = CGAffineTransformRotate(self.contentView.transform, M_PI / 250.0);
    4. }

    6> 选择一个星座,点击中间的开始选号,当转盘停止旋转时,星座指针指向正上方

    . 点击开始选号,创建基本动画,将转盘旋转N圈

    1. //开始选号
    2. - (IBAction)chooseNum:(id)sender {
    3. //创建动画对象
    4. CABasicAnimation *anim = [CABasicAnimation animation];
    5. //设置动画的属性值
    6. anim.keyPath = @"transform.rotation";
    7. anim.toValue = @(M_PI * 4);
    8. anim.delegate = self;
    9. anim.duration = 0.5;
    10. [self.contentView.layer addAnimation:anim forKey:nil];
    11. }

    . 实现动画停止后的方法,来将指针回转到正上方

    1. //当动画结束时调用.
    2. -(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
    3. //动画结束时,选择按钮指向最上方.
    4. //动画结束时,让选中的按钮倒着旋转回去.
    5. CGAffineTransform transform = self.preBtn.transform;
    6. CGFloat angle = atan2(transform.b, transform.a);
    7. self.contentView.transform = CGAffineTransformMakeRotation(-angle);
    8. }


  • 相关阅读:
    [BZOJ4698][SDOI2008]Sandy的卡片(后缀自动机)
    [NOI2015]小园丁与老司机(DP+上下界最小流)
    [BZOJ2007][NOI2010]海拔(对偶图最短路)
    [NOI2018]屠龙勇士(exCRT)
    [NOI2018]归程(可持久化并查集,Kruskal重构树)
    [BZOJ2823][BZOJ1336][BZOJ1337]最小圆覆盖(随机增量法)
    [BZOJ1069][SCOI2007]最大土地面积(水平扫描法求凸包+旋转卡壳)
    [BZOJ1143][CTSC2008]祭祀river(Dilworth定理+二分图匹配)
    [BZOJ3160]万径人踪灭(FFT+Manacher)
    [NOI2015]寿司晚宴
  • 原文地址:https://www.cnblogs.com/Xfsrn/p/5000359.html
Copyright © 2020-2023  润新知