《幸運大轉盤》代碼分享


《幸運大轉盤》有一句代碼是這樣的:

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 <UIKit/UIKit.h>

@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

 

運行結果賞析:



 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章