《幸运大转盘》代码分享


《幸运大转盘》有一句代码是这样的:

self.rotateView.transform = CGAffineTransformMakeRotation(-angle);

它出现在延迟派遣消息 dispatch_after 里面,然而你真的看懂它了吗?

本文将揭秘这句代码的真相!红字黄底标出!


#import "ViewController.h"

#import "ZHYView.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {

    [super viewDidLoad];

    // 让控制器的view以拉伸的方式设置成图片

    self.view.layer.contents = (__bridge id)([UIImage imageNamed:@"LuckyBackground"].CGImage);

    // 创建转盘的对象

    ZHYView *rotateView= [ZHYView rotateImage];

    // 设置转盘在屏幕上居中显示

    rotateView.center = self.view.center;

    // 把装盘添加到控制器当中

    [self.view addSubview:rotateView];

    // 程序一运行就让锯齿图片旋转

    [rotateView startRotate];   

}

// 设置状态栏样式为白色字体,更好看一些

-(UIStatusBarStyle)preferredStatusBarStyle{  

    return UIStatusBarStyleLightContent;

}

@end

#import

@interface CZView : UIView

+ (instancetype)rotateView;

- (void)startRotate;

@end

 

#import "ZHYView.h"

#define kButtonCount 12

@interface ZHYView () <UIAlertViewDelegate>

@property (weak, nonatomic) IBOutlet UIImageView *rotateView;

@property (nonatomic,weak) UIButton *lastButton;

@property (nonatomic,strong) CADisplayLink *link;

@end

 

@implementation ZHYView

// 开始旋转

-(void)startRotate{

    // CADisplayLink刷帧,默认每秒刷新60次,该定时器创建之后,默认是不会执行的,需要把它加载到主消息循环中才会被执行

    CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(rotate)];

    // CADisplayLink定时器加载到主循环中

    [link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

    // 给属性link赋值,便于后面对CADisplayLink定时器对象link进行设置

    self.link = link;

}

 

// 旋转

-(void)rotate{

   

    CGFloat round = 5;

    CGFloat angle = 2 * M_PI / 60 / round;

    // 设置CADisplayLink定时器对象link,让rotateView5秒转一圈

    self.rotateView.transform = CGAffineTransformRotate(self.rotateView.transform, angle);

   

}

 

// 开始选号的点击事件

- (IBAction)pickNumber:(UIButton *)sender {

   

    // 如果没有选中任何一个button的时候不旋转

    if (!self.lastButton) {

        return;

    }

    // CADisplayLink定时器对象link暂停下来,避免UIAlertView的代理方法(点击弹出提示框的确定按钮后执行的方法)结束后选中的button跳屏

    self.link.paused = YES;

    // 关掉大转盘的用户交互

    self.userInteractionEnabled = NO;

    // 创建一个基本动画,让转盘快速旋转以备选号

   

    CABasicAnimation *ani = [[CABasicAnimation alloc] init];

    // 设置关键路径

    ani.keyPath = @"transform.rotation";

    // 记录每个button对应的初始角度

    CGFloat angle = self.lastButton.tag * 2 * M_PI / 12;

    // 设置属性toValue为了让选中的button快速旋转后指向转盘的正上方

    ani.toValue = @(2 * M_PI * 5 - angle);

    // 设置快速旋转动画的持续时间

    ani.duration = 2;

    // 修改动画的默认模式,不让动画在结束后复位

    ani.fillMode = kCAFillModeForwards;

    // BOOL属性不设置的话,动画默认修改不成功;

    ani.removedOnCompletion = NO;

    // 将动画添加到layer

    [self.rotateView.layer addAnimation:ani forKey:@"key"];

   

    // 延迟 ani.duration 时间后派遣{}中的消息

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(ani.duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

 

        /*

         这里需要详细解释一下:

         假设:

         原始角度:狮子座在最上面: 0

         当你点击大转盘的星座按钮时狮子座的角度:A

         当你接着点击选号按钮时狮子座的角度:B

         动画结束后狮子座的位置被设定了在最上面:0

         如果下面这句代码不设置,就会发生跳屏现象,即选中的按钮会跳到角度 B

         */

       

        self.rotateView.transform = CGAffineTransformMakeRotation(-angle);

        // 创建提示视图,显示信息

        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"幸运大转盘,赚的就是你" message:@"13579" delegate:self cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];

        // 显示提示视图

        [alert show];

        // 开启大转盘的用户交互

        self.userInteractionEnabled = YES;

    });

}

 

// UIAlertView的代理方法,在点击cancelButtonTitle:@"确定"的时候会执行

-(void)alertView:(UIAlertView *)alertViewclickedButtonAtIndex:(NSInteger)buttonIndex{

   

    // 移除指定AnimationForKey:@"key"的动画

    [self.rotateView.layer removeAnimationForKey:@"key"];

   

    /*

        其实在大转盘选号结束后这么设置可以让大转盘恢复初始状态[随便玩玩]

        self.lastButton.selected = NO;

        self.lastButton = nil;

     */

   

    // CADisplayLink定时器对象link重新计时

    self.link.paused = NO;

}

 

// button的点击事件

-(void)clickButton:(UIButton *)button{

    // 取消上一个button的选中状态

    self.lastButton.selected = NO;

    // 设置点击到的button的选中状态

    button.selected = YES;

    // 点击事件的最后将当前button赋值给lastbutton

    self.lastButton = button;

}

 

// 布局子控件

-(void)layoutSubviews{

    // 遍历锯齿图片里面的子控件(button)设置frame

    for (int i = 0; i < self.rotateView.subviews.count; i++) {

        UIButton *button = self.rotateView.subviews[i];

        CGFloat buttonCenterX = self.bounds.size.width * 0.5;

        CGFloat buttonCenterY = self.bounds.size.height * 0.5;

        button.frame = CGRectMake(0, 0, 68, 143);

        button.center = CGPointMake(buttonCenterX,buttonCenterY);

       

        // 设置每个button的初始角度

        CGFloat angle = i * 2 * M_PI / 12;

        // 将每个button散开

        button.transform = CGAffineTransformMakeRotation(angle);

        // 设置button的内边距

        [button setContentEdgeInsets:UIEdgeInsetsMake(-44, 0, 0, 0)];

    }

}

 

//裁剪图片

-(UIImage *)clipImage:(UIImage *)image withIndex:(int)index{

    //设置将要从image获取的裁剪到的图片的frame,注意宽高需要乘以设备的缩放因子

    CGFloat w = image.size.width / 12 * [UIScreen mainScreen].scale;

    CGFloat h = image.size.height * [UIScreen mainScreen].scale;

    CGFloat x = index * w;

    CGFloat y = 0;

   

    // 获取裁剪imagerect部分裁剪的图片

    CGImageRef imageRef = CGImageCreateWithImageInRect(image.CGImage, CGRectMake(x, y, w, h));

    // 通过CGImageRefimage转成UIimagescale是缩放因子,需要手动调试出一个合适的值,orientation是一个方向枚举,0表示默认方向

    return [UIImage imageWithCGImage:imageRef scale:2 orientation:0];

}

 

//nib加载

-(void)awakeFromNib{

   

    // 创建12button

    for (int i = 0; i < kButtonCount; i++) {

        // 创建button

        UIButton * button = [[UIButton alloc] init];

        // 修改button的锚点为中间最下

        button.layer.anchorPoint = CGPointMake(0.5, 1);

        // 绑定buttontag属性,便于确定每个button的初始位置角度

        button.tag = i;

       

        // 加载button默认和选中两个状态的原图

        UIImage *image = [UIImage imageNamed:@"LuckyAstrology"];

        UIImage *imagePress = [UIImage imageNamed:@"LuckyAstrologyPressed"];

        // 获取通过自定义的方法将两个原图按顺序裁剪的图片

        image = [self clipImage:image withIndex:i];

        imagePress = [self clipImage:imagePress withIndex:i];

       

        // button的默认和选中状态设置裁剪好的图片

        [button setImage:image forState:UIControlStateNormal];

        [button setImage:imagePress forState:UIControlStateSelected];

        // 设置button的背景图片

        [button setBackgroundImage:[UIImage imageNamed:@"LuckyRototeSelected"] forState:UIControlStateSelected];

       

        // 设置button的点击事件clickButton:

        [button addTarget:self action:@selector(clickButton:) forControlEvents:UIControlEventTouchUpInside];

        // button添加到rotateView

        [self.rotateView addSubview:button];

    }

}

 

// 一个返回值为大转盘对象的类方法,便于外部访问

+(instancetype)rotateImage{

    return [[NSBundle mainBundle] loadNibNamed:@"ZHYView" owner:nil options:nil][0];

}

@end

 

运行结果赏析:

《幸运大转盘》代码分享_第1张图片

《幸运大转盘》代码分享_第2张图片

 


你可能感兴趣的:(UI)