没事正常旋转,点击开始选号码,快速旋转网上烂大街的有写网易彩票的圆盘动画,今天我也记录一下,防止遗忘
基本思路
1.搭建基本的wheel
2.让第二层(大黄色的圆盘)的旋转起来
3.往第二层(大黄色)中添加12个按钮,
3.1(谈谈awakeFromNib
,和init?(coder aDecoder: NSCoder)
的顺序和区别)
3.2 锚点的设置,和point的实际用法
3.3 选择按钮经典3部曲
4.将大图剪切成小图,讲讲CGImageCreateWithImageInRect
这个方法的使用,还有像素比,以及image.size
到底是啥,还有为毛线[UIScreen mainScreen].scale
5.重写btn方法,更改内部image的尺寸
6.给btn一个选中的image的照片(还是按照4那样切图)
7.重写setHightLighit方法 (但是没写明白)
8.UIControlEvents.TouchDown
和UIControlEvents.TouchUpInside
的区别
9.为装盘添加2个方法,开始和结束
10.CADisplayLink
和NSTimer
的区别和使用情景
10.避免多个定时器同时工作,会出什么问题?
11.细节,(大黄圆盘)交互yes,no
1.搭建基本的wheel
直接封装一个view,叫做RoundWheel
,xib脱线布局,比较方便快捷
2.让第二层(大黄色的圆盘)的旋转起来
对外提供一个开始方法,直接选择
func startRotating()
{
let anim = CABasicAnimation()
anim.keyPath = "transform.rotation"
anim.toValue = M_PI*2
anim.duration = 3
anim.removedOnCompletion = false
anim.repeatCount = MAXFLOAT
anim.fillMode = kCAFillModeForwards
//我现在让最低部的view旋转了,所以“开始按钮”才跟着旋转
layer.addAnimation(anim, forKey: "rotationWheel")
本图片有点问题,本图片是让最底下的view旋转了,所以“开始按钮”也跟这转,如果你是是黄色的view.layer执行动画的,就不会这样了开始按钮也在上边,然后就跟着转了
3.往第二层(大黄色)中添加12个按钮
平面图 立体图先做出这样的效果
override func awakeFromNib() {
//创建12个按钮
createSubviews()
centerView.userInteractionEnabled = true
}
private func createSubviews(){
for index in 0 ..< 12
{
let btn = RWButton(
btn.addTarget(self, action: "btnBeSelected:", forControlEvents: UIControlEvents.TouchUpInside)
//2.设置背景颜色
let randomR = Float(arc4random_uniform(255))/255.0
let randomG = Float(arc4random_uniform(255))/255.0
let randomB = Float(arc4random_uniform(255))/255.0
let randomColor = UIColor.init(colorLiteralRed: randomR, green: randomG, blue: randomB, alpha: 1)
btn.backgroundColor = randomColor
}
}
布局的代码
override func layoutSubviews() {
super.layoutSubviews()
let bW:CGFloat = 68
let bH:CGFloat = 143
let bY:CGFloat = 0
let bX = (self.frame.width - bW) * 0.5
for index in 0 ..< 12
{
//1.设置基本的frame
let btn = centerView.subviews[index] as! RWButton
btn.frame = CGRectMake(bX, bY, bW, bH)
}
}
这里讲解一下
awakeFromNib
和init?(coder aDecoder: NSCoder)的顺序和区别
- 1.如果A控制器(或者View)想通过xib加载B(view),那么一定会调用B的
init?(coder aDecoder: NSCoder
- 2.如果C(view)是通过xib加载出来的,那么一定会调用
awakeFromNib
方法 - 3.B(View)的创建,可能是纯代码写的,一定会调用
initWithFrame:
方法,如果是通过xib创建的,一定会调用init?(coder aDecoder: NSCoder
方法 - 4.
init?(coder aDecoder: NSCoder
县调用,awakeFromNib
后调用(执行到这里,xib拉出来的连线的view,才不为空,在init(coder aDecoder)的时候,连线出来的view是空的)
重新布局
override func layoutSubviews() {
super.layoutSubviews()
let bW:CGFloat = 68
let bH:CGFloat = 143
let bY:CGFloat = 0
let bX = (self.frame.width - bW) * 0.5
for index in 0 ..< 12
{
//1.设置基本的frame
let btn = centerView.subviews[index] as! RWButton
btn.frame = CGRectMake(bX, bY, bW, bH)
//2.设置transfrom的属性
let angle = (Double(index) * 2) * M_PI / 12.0
btn.layer.anchorPoint = CGPointMake(0.5, 1)
btn.layer.position = CGPointMake(self.frame.width*0.5, self.frame.height*0.5)
btn.transform = CGAffineTransformMakeRotation(CGFloat(angle))
}
}
做成可以点击每一个按钮,的样子,忽略上班的文字,我要的是selected的选中的效果注意
1.一个view锚点默认值是(0.5,0.5)
2.btn.layer.position
是锚点的位置
3.所有的旋转,或者是平移,以及拉伸都是沿着锚地做的
4.设置每个btn锚点的位置在(0.5,1),锚点的取值范围是0到1
5.一共12份,所以360°/12 = 30°
1.设置每个按钮的选中状态的背景照片
btn .setBackgroundImage(UIImage(named:"LuckyRototeSelected"), forState: UIControlState.Selected)
背景照片
2.经典的选中btn三部曲
2.0 准备工作. 首先要有一个全局weak属性selectedButton
.还有给12个按钮点击事件btn.addTarget(self, action: "btnBeSelected:", forControlEvents: UIControlEvents.TouchUpInside)
- 2.1 先让selectedButton选中状态为false
- 2.2 让刚刚点击的btn的selected等于true
- 2.3 最后让selectedButton指向bin
func btnBeSelected(btn:UIButton){
//1.先让之前选中的按钮取消选中
selectedBtn?.selected = false
//2.让刚刚点的按钮设置成选中状态
btn.selected = true
//3.使用全局变量保存刚刚点中的btn
selectedBtn = btn
}
简单快捷有效,我看过很多程序员,都写的特别麻烦~
4.将大图剪切成小图,设置成btn.image
UI为了减小包的大小,只给了一张图片,让我们自己剪切,那就切割12份啊,貌似很简单,其实很多坑 private func createSubviews(){
for index in 0 ..< 12
{
let btn = UIButton()
btn .setBackgroundImage(UIImage(named:"LuckyRototeSelected"), forState: UIControlState.Selected)
btn.addTarget(self, action: "btnBeSelected:", forControlEvents: UIControlEvents.TouchUpInside)
//3.设置背景照片
let smallImage = UIImage(named: "LuckyAstrology")
let iW = (smallImage?.size.width)!
let iH = (smallImage?.size.height)!
//4.设置normal情况下的image
let rect = CGRectMake(CGFloat(index) * iW , 0, iW, iH)
let norImageRef = CGImageCreateWithImageInRect(smallImage?.CGImage,rect)
btn .setImage(UIImage(CGImage:norImageRef!), forState: UIControlState.Normal)
//5.设置select样式的image
let selectedSmallImage = UIImage(named: "LuckyAstrologyPressed")
let seletedImageRef = CGImageCreateWithImageInRect(selectedSmallImage?.CGImage, rect)
btn.setImage(UIImage(CGImage: seletedImageRef!), forState: UIControlState.Selected)
centerView.addSubview(btn)
}
}
乱七八糟,其实细看,只有4分之一的图片
问题很复杂,就是几倍图的问题。
1.在ios项目中,我们使用的是点坐标(dx)
2.在c语言函数中,我们使用的是像素(px)单位
CGImageCreateWithImageInRect
是c语言函数
3.ios中有1倍图,2倍图,3倍图
4.我们将图片使用在项目中,看他的大小,是1倍图的尺寸
5.但是retain
屏幕,是2倍图,plus是3倍图,不同情况,系统加载的图片是不一样的,所以获取的照片的尺寸一定是不同的!
6.获取当前测试机是几倍图的终结者UIScreen.mainScreen().scale
,判断屏幕尺寸的方法有的时候不准,但是这个属性一定准, 4s,5,6,6s都是 2倍图,plus是三倍图,我打印了~~
7.切割图片的时候,通过smallImage?.size.width
获取的是1倍图的尺寸
8.CGImageCreateWithImageInRect(smallImage?.CGImage,rect)
第一个参数是要切割什么图(加载相应的几倍图),第二个参数是用什么rect切割
9.做了一份打印 ,结果如下
//尺寸 Optional((480.0, 46.0)) 当前的比例 2.0
//尺寸 Optional((480.0, 46.0)) 当前的比例 3.0
一倍图的尺寸10.这个解释了7,8
11.切割图片的时候,rect的宽高要乘以 屏幕比例
代理更改如下
//3.设置背景照片
let smallImage = UIImage(named: "LuckyAstrology")
let iW = (smallImage?.size.width)! / 12.0 * UIScreen.mainScreen().scale
let iH = (smallImage?.size.height)! * UIScreen.mainScreen().scale
print("\(smallImage) 尺寸 \(smallImage?.size) 当前的比例 \(UIScreen.mainScreen().scale)")
//4.设置normal情况下的image
let rect = CGRectMake(CGFloat(index) * iW , 0, iW, iH)
let norImageRef = CGImageCreateWithImageInRect(smallImage?.CGImage,rect)
btn .setImage(UIImage(CGImage:norImageRef!), forState: UIControlState.Normal)
//尺寸 Optional((480.0, 46.0)) 当前的比例 2.0
//尺寸 Optional((480.0, 46.0)) 当前的比例 3.0
//5.设置select样式的image
let selectedSmallImage = UIImage(named: "LuckyAstrologyPressed")
let seletedImageRef = CGImageCreateWithImageInRect(selectedSmallImage?.CGImage, rect)
btn.setImage(UIImage(CGImage: seletedImageRef!), forState: UIControlState.Selected)
centerView.addSubview(btn)
对大图进行了乘以比例的切割后的样子
5.重写btn方法,更改内部image的尺寸
调整image位置
override func imageRectForContentRect(contentRect: CGRect) -> CGRect {
let iY:CGFloat = 20
let iW:CGFloat = 40
let iH:CGFloat = 47
let iX:CGFloat = (contentRect.size.width - iH)*0.5
return CGRectMake(iX, iY, iW, iH)
}
调整image位置后的样子
6.给btn一个选中的image的照片(还是按照4那样切图)
let selectedSmallImage = UIImage(named: "LuckyAstrologyPressed")
let seletedImageRef = CGImageCreateWithImageInRect(selectedSmallImage?.CGImage, rect)
btn.setImage(UIImage(CGImage: seletedImageRef!), forState: UIControlState.Selected)
7.重写setHightLighit方法 (但是没写明白)
为什么要重写setHightLighit
这个方法?因为当你按下去的时候,出去高亮状态,如果你重写了这个方法,就不会有高亮的状态,现在的项目中你按住某个btn,是黑色的,有bug
oc中自定义一个button,内部这样写,就不会有高亮了
- (void)setHighlighted:(BOOL)highlighted{
}
swift中我不会好尴尬,就是截获set方法,然后不让父类实现这个方法 ,我去监听了set,但是感觉内部还是执行了super.setHightlighted方法~~有木有知道这个的同学,给我讲讲吧,谢谢哈
//swift中截获set方法,但是我怀疑这里面,已经调用了父类的set方法,
override var highlighted: Bool{
didSet{
}
}
8 UIControlEvents.TouchDown
和UIControlEvents.TouchUpInside
的区别
这个想必大家都知道,强者是一按下去,就执行,后者是,按下去,让后抬起来,在执行。现在想执行的效果是--按下去就被选中,就执行
func btnBeSelected(btn:UIButton){
//1.先让之前选中的按钮取消选中
selectedBtn?.selected = false
//2.让刚刚点的按钮设置成选中状态
btn.selected = true
//3.使用全局变量保存刚刚点中的btn
selectedBtn = btn
}
代码是这样写的,但是效果不是这样的~不知道咋回事,有时间再看看
9.为装盘添加2个方法,开始和结束
很简单,就是开始和结束
/**
开始旋转
*/
func startRotating()
{
if (self.link != nil)
{
return
}
//1.生成定时器
let link = CADisplayLink.init(target: self, selector: "update")
link.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSDefaultRunLoopMode)
self.link = link
}
func update()
{
centerView.transform = CGAffineTransformRotate(centerView.transform, CGFloat(M_PI/300.0))
}
func endRotationing()
{
link?.invalidate()
link = nil
}
10.CADisplayLink和NSTimer的区别和使用情景
为甚使用的是
CADisplayLink
?
这个方法一秒钟调用60次,可以非常快速,而NSTimer
调用的是1秒的,不能像前者调用频率那么快
这两个的使用场景是什么?
特别快的使用CADisplayLink
,一秒及以后的调用NSTimer
11.避免多个定时器同时工作,会出什么问题?
开始的方法中必须调用这个
if (self.link != nil)
{
return
}
防止多个定时器同事工作,一调用开始,先判断是不是有值,如果有,退出,否则多个在一起,越来越来转速,叠加的~
11.细节,(大黄圆盘)交互yes,no
体现有没有工作经验,可以看看有没有这个大黄圆盘的交互,就是当我们点击中间开始按钮的时候,圆盘要快速旋转,但是那时候是不饿能够点击圆盘的任何按钮的,所以,交互式no,其他事yes
12.点击开始按钮
这段代码有点意思,就是先知道,layer层的动画都是假象,点击某个按钮都是不准的,但是UIView的动画是真实的,可以点击到具体的那个Btn的,但是这里我们就是让他快速的旋转,所以给个layer层就好了
这里使用到的是timingFunction,进入缓慢,出来缓慢,我们设置3次,所以我们旋转圈数和时间都乘以3,然后第一圈第三圈都是缓慢的~
@IBAction func centerBtnClick(sender: AnyObject) {
endRotationing()
let anim = CABasicAnimation()
anim.keyPath = "transform.rotation"
anim.toValue = M_PI*2*3
anim.duration = 1.5*3
anim.removedOnCompletion = false
// anim.repeatCount = 3
anim.fillMode = kCAFillModeForwards
anim.timingFunction = CAMediaTimingFunction.init(name: kCAMediaTimingFunctionEaseInEaseOut)
anim.delegate = self
centerView.layer.addAnimation(anim, forKey: "rotationWheel")
}
监听了结束的代理方法,也执行了,但是2秒之后还是不转,没搞懂,欢迎指教~
override func animationDidStop(anim: CAAnimation, finished flag: Bool) {
let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(2 * Double(NSEC_PER_SEC)))
dispatch_after(delayTime, dispatch_get_main_queue()) { () -> Void in
// print("切换到首页")
self.startRotating()
}
}
底部的btn都是等宽度的,点击可能有误
右侧图红色的地方不可以点击,所以返回nil点击底部的时候,因为btn都是一样大的,重叠了,如何解决?
实际上我们可以让他的底部不能点击,只能点击上边,那么我们就要重写but的方法,设置那些区域可以点击,那些不行
/**
寻找合适的view(可以判断点view的那个位置是不可以点击的,那个是可以点击的)
:param: point 当前点所在位置
:param: event 点击事件
:returns: 返回合适的view
*/
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
let x:CGFloat = 0
let y:CGFloat = 0
let w:CGFloat = frame.width
let h:CGFloat = frame.height*0.5
let rect = CGRectMake(x, y, w, h)
if(CGRectContainsPoint(rect, point)){
return super.hitTest(point, withEvent: event)
}else{
return nil
}
}
网友评论