最近在学习核心动画部分,今天学习了转盘的实现,在这里记录一下实现过程。
首先是用xib描述一个转盘view,分为三层,底部是一张底图背景色的图片,中间的是要旋转的ImageView,最上面是一个按钮,如图:
新建转盘view对象WheelView:在转盘view里要提供两个方法给外部使用,开始旋转和暂停旋转在构造方法中去加载xib。
#import <UIKit/UIKit.h> @interface WheelView : UIView + (instancetype)wheelView; - (void)start; - (void)pause; @end
+ (instancetype)wheelView { return [[[NSBundle mainBundle]loadNibNamed:NSStringFromClass(self) owner:nil options:nil]lastObject]; }
初始化完WheelView后去给转盘添加按钮,这些事情要在哪做?awakeFromNib方法还是initWithCoder?dan‘an(这个也是敏感词?)是awakeFromNib,因为在自定义view里面可能需要在类扩展中添加一些属性,跟xib中的view连线,但是initWithCoder调用的时候连线还没有完成好,而当调用awakeFromNib时,xib已经完全加载好了。
定义一个成员变量中间层的veiw,在这个view上来添加按钮,一个保存被选中的按钮,一个定时器
#import "WheelView.h" #import "WheelButton.h" @interface WheelView () /** centerview */ @property(nonatomic, weak) IBOutlet UIImageView *centerView; //选中的按钮 @property(nonatomic, weak) UIButton *selectBtn; /** 定时器 */ @property(nonatomic, strong) CADisplayLink *link; @end
定时器进行懒加载:
- (CADisplayLink *)link { if (_link == nil) { _link = [CADisplayLink displayLinkWithTarget:self selector:@selector(angleChange)]; //添加到主运行循环 [_link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; } return _link; }
定时器中执行的方法是改变中间层的view的transfrom属性,进行旋转:
- (void)angleChange { //规定1秒旋转45度 //计算每一次调用旋转多少度:这个方法是一秒调用60次 CGFloat angle = (45 / 60.0) * M_PI / 180.0; _centerView.transform = CGAffineTransformRotate(_centerView.transform, angle); }
添加按钮的过程:
- (void)awakeFromNib { //允许UIImageView可以与用户交互 _centerView.userInteractionEnabled = YES; CGFloat btnW = 68; CGFloat btnH = 143; CGFloat wh = self.bounds.size.width; //加载星座大图片 UIImage *bigImage = [UIImage imageNamed:@"LuckyAstrology"]; //加载星座选中时候的大图片 UIImage *bigSelImage = [UIImage imageNamed:@"LuckyAstrologyPressed"]; //这里要注意是像素,而不是点 //获取像素与点的比例 CGFloat scale = [UIScreen mainScreen].scale; CGFloat imageW = bigImage.size.width / 12 * scale; CGFloat imageH = bigImage.size.height * scale; //添加按钮 for (int i = 0; i < 12; i++) { WheelButton *btn = [WheelButton buttonWithType:UIButtonTypeCustom]; //设置按钮的位置 btn.layer.anchorPoint = CGPointMake(0.5, 1); btn.layer.bounds = CGRectMake(0, 0, btnW, btnH); btn.layer.position = CGPointMake(wh * 0.5, wh * 0.5); //旋转角度 CGFloat angle = (30 * i) / 180.0 * M_PI; btn.transform = CGAffineTransformMakeRotation(angle); [_centerView addSubview:btn]; //设置按钮的图片 //计算裁剪区域 CGRect clipR = CGRectMake(i * imageW, 0, imageW, imageH); //裁剪图片 CGImageRef imgR = CGImageCreateWithImageInRect(bigImage.CGImage, clipR); UIImage *image = [UIImage imageWithCGImage:imgR]; //设置按钮图片 [btn setImage:image forState:UIControlStateNormal]; //设置选中时候的图片 imgR = CGImageCreateWithImageInRect(bigSelImage.CGImage, clipR); image = [UIImage imageWithCGImage:imgR]; //设置选中时的背景图片 [btn setBackgroundImage:[UIImage imageNamed:@"LuckyRototeSelected"] forState:UIControlStateSelected]; //设置按钮尺寸 [btn setImage:image forState:UIControlStateSelected]; [btn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside]; //默认选中第一个 if (i == 0) { [self btnClick:btn]; } } } - (void)btnClick:(UIButton *)btn { _selectBtn.selected = NO; btn.selected = YES; _selectBtn = btn; }
由于UIButton是不能设置它里面的image的尺寸的,所以这里需要自定义一个UIButton:WheelButton,继承自UIButton,它里面主要重写一些系统UIButton不能做到的事情
#import "WheelButton.h" @implementation WheelButton /** * 设置UIImageView的尺寸,contentRect是按钮的尺寸 */ - (CGRect)imageRectForContentRect:(CGRect)contentRect { //计算UIImageView控件的尺寸 CGFloat imageW = 40; CGFloat imageH = 46; CGFloat imageX = (contentRect.size.width - imageW) * 0.5; CGFloat imageY = 20; return CGRectMake(imageX, imageY, imageW, imageH); } //重写该方法目的:取消图片高亮状态 - (void)setHighlighted:(BOOL)highlighted { } // 寻找最合适的view,让Button只能点击上半部分 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { CGFloat btnW = self.bounds.size.width; CGFloat btnH = self.bounds.size.height; CGFloat x = 0; CGFloat y = btnH / 2; CGFloat w = btnW; CGFloat h = y; CGRect rect = CGRectMake(x, y, w, h); if (CGRectContainsPoint(rect, point)) { return nil; }else{ return [super hitTest:point withEvent:event]; } } @end
最后封装转盘view的开始旋转和暂停旋转的方法
- (void)start { self.link.paused = NO; } - (void)pause { self.link.paused = YES; }
控制器中在主窗口添加一个wheelview
#import "ViewController.h" #import "WheelView.h" @interface ViewController () /** 转盘view */ @property(nonatomic, weak) WheelView *wheelView; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; //添加转盘 WheelView *wheelView = [WheelView wheelView]; wheelView.center = self.view.center; [self.view addSubview:wheelView]; _wheelView = wheelView; } #pragma mark - 开始旋转 - (IBAction)start:(id)sender { [_wheelView start]; } #pragma mark - 暂停旋转 - (IBAction)pause:(id)sender { [_wheelView pause]; } @end
这样就实现完了。效果图: