網(wǎng)上爛大街的有寫網(wǎng)易彩票的圓盤動(dòng)畫枝秤,今天我也記錄一下兼吓,防止遺忘
基本思路
1.搭建基本的wheel
2.讓第二層(大黃色的圓盤)的旋轉(zhuǎn)起來
3.往第二層(大黃色)中添加12個(gè)按鈕沉帮,
3.1(談?wù)?code>awakeFromNib,和init?(coder aDecoder: NSCoder)
的順序和區(qū)別)
3.2 錨點(diǎn)的設(shè)置珍剑,和point的實(shí)際用法
3.3 選擇按鈕經(jīng)典3部曲
4.將大圖剪切成小圖掸宛,講講CGImageCreateWithImageInRect
這個(gè)方法的使用,還有像素比招拙,以及image.size
到底是啥唧瘾,還有為毛線[UIScreen mainScreen].scale
5.重寫btn方法,更改內(nèi)部image的尺寸
6.給btn一個(gè)選中的image的照片(還是按照4那樣切圖)
7.重寫setHightLighit方法 (但是沒寫明白)
8.UIControlEvents.TouchDown
和UIControlEvents.TouchUpInside
的區(qū)別
9.為裝盤添加2個(gè)方法别凤,開始和結(jié)束
10.CADisplayLink
和NSTimer
的區(qū)別和使用情景
10.避免多個(gè)定時(shí)器同時(shí)工作饰序,會(huì)出什么問題?
11.細(xì)節(jié)规哪,(大黃圓盤)交互yes求豫,no
1.搭建基本的wheel
直接封裝一個(gè)view,叫做RoundWheel
,xib脫線布局诉稍,比較方便快捷
2.讓第二層(大黃色的圓盤)的旋轉(zhuǎn)起來
對外提供一個(gè)開始方法,直接選擇
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
//我現(xiàn)在讓最低部的view旋轉(zhuǎn)了杯巨,所以“開始按鈕”才跟著旋轉(zhuǎn)
layer.addAnimation(anim, forKey: "rotationWheel")
3.往第二層(大黃色)中添加12個(gè)按鈕
先做出這樣的效果
override func awakeFromNib() {
//創(chuàng)建12個(gè)按鈕
createSubviews()
centerView.userInteractionEnabled = true
}
private func createSubviews(){
for index in 0 ..< 12
{
let btn = RWButton(
btn.addTarget(self, action: "btnBeSelected:", forControlEvents: UIControlEvents.TouchUpInside)
//2.設(shè)置背景顏色
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.設(shè)置基本的frame
let btn = centerView.subviews[index] as! RWButton
btn.frame = CGRectMake(bX, bY, bW, bH)
}
}
這里講解一下
awakeFromNib
和init?(coder aDecoder: NSCoder)的順序和區(qū)別
- 1.如果A控制器(或者View)想通過xib加載B(view),那么一定會(huì)調(diào)用B的
init?(coder aDecoder: NSCoder
- 2.如果C(view)是通過xib加載出來的箫章,那么一定會(huì)調(diào)用
awakeFromNib
方法 - 3.B(View)的創(chuàng)建烙荷,可能是純代碼寫的,一定會(huì)調(diào)用
initWithFrame:
方法檬寂,如果是通過xib創(chuàng)建的终抽,一定會(huì)調(diào)用init?(coder aDecoder: NSCoder
方法 - 4.
init?(coder aDecoder: NSCoder
縣調(diào)用,awakeFromNib
后調(diào)用(執(zhí)行到這里桶至,xib拉出來的連線的view昼伴,才不為空,在init(coder aDecoder)的時(shí)候镣屹,連線出來的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.設(shè)置基本的frame
let btn = centerView.subviews[index] as! RWButton
btn.frame = CGRectMake(bX, bY, bW, bH)
//2.設(shè)置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))
}
}
注意
1.一個(gè)view錨點(diǎn)默認(rèn)值是(0.5,0.5)
2.btn.layer.position
是錨點(diǎn)的位置
3.所有的旋轉(zhuǎn)圃郊,或者是平移,以及拉伸都是沿著錨地做的
4.設(shè)置每個(gè)btn錨點(diǎn)的位置在(0.5女蜈,1)持舆,錨點(diǎn)的取值范圍是0到1
5.一共12份,所以360°/12 = 30°
1.設(shè)置每個(gè)按鈕的選中狀態(tài)的背景照片
btn .setBackgroundImage(UIImage(named:"LuckyRototeSelected"), forState: UIControlState.Selected)
2.經(jīng)典的選中btn三部曲
2.0 準(zhǔn)備工作. 首先要有一個(gè)全局weak屬性selectedButton
.還有給12個(gè)按鈕點(diǎn)擊事件btn.addTarget(self, action: "btnBeSelected:", forControlEvents: UIControlEvents.TouchUpInside)
- 2.1 先讓selectedButton選中狀態(tài)為false
- 2.2 讓剛剛點(diǎn)擊的btn的selected等于true
- 2.3 最后讓selectedButton指向bin
func btnBeSelected(btn:UIButton){
//1.先讓之前選中的按鈕取消選中
selectedBtn?.selected = false
//2.讓剛剛點(diǎn)的按鈕設(shè)置成選中狀態(tài)
btn.selected = true
//3.使用全局變量保存剛剛點(diǎn)中的btn
selectedBtn = btn
}
簡單快捷有效覆山,我看過很多程序員竹伸,都寫的特別麻煩~
4.將大圖剪切成小圖,設(shè)置成btn.image
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.設(shè)置背景照片
let smallImage = UIImage(named: "LuckyAstrology")
let iW = (smallImage?.size.width)!
let iH = (smallImage?.size.height)!
//4.設(shè)置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.設(shè)置select樣式的image
let selectedSmallImage = UIImage(named: "LuckyAstrologyPressed")
let seletedImageRef = CGImageCreateWithImageInRect(selectedSmallImage?.CGImage, rect)
btn.setImage(UIImage(CGImage: seletedImageRef!), forState: UIControlState.Selected)
centerView.addSubview(btn)
}
}
問題很復(fù)雜耙蔑,就是幾倍圖的問題见妒。
1.在ios項(xiàng)目中,我們使用的是點(diǎn)坐標(biāo)(dx)
2.在c語言函數(shù)中甸陌,我們使用的是像素(px)單位
CGImageCreateWithImageInRect
是c語言函數(shù)
3.ios中有1倍圖须揣,2倍圖,3倍圖
4.我們將圖片使用在項(xiàng)目中钱豁,看他的大小耻卡,是1倍圖的尺寸
5.但是retain
屏幕,是2倍圖牲尺,plus是3倍圖卵酪,不同情況幌蚊,系統(tǒng)加載的圖片是不一樣的,所以獲取的照片的尺寸一定是不同的溃卡!
6.獲取當(dāng)前測試機(jī)是幾倍圖的終結(jié)者UIScreen.mainScreen().scale
溢豆,判斷屏幕尺寸的方法有的時(shí)候不準(zhǔn),但是這個(gè)屬性一定準(zhǔn), 4s,5,6瘸羡,6s都是 2倍圖漩仙,plus是三倍圖,我打印了~~
7.切割圖片的時(shí)候犹赖,通過smallImage?.size.width
獲取的是1倍圖的尺寸
8.CGImageCreateWithImageInRect(smallImage?.CGImage,rect)
第一個(gè)參數(shù)是要切割什么圖(加載相應(yīng)的幾倍圖)队他,第二個(gè)參數(shù)是用什么rect切割
9.做了一份打印 ,結(jié)果如下
//尺寸 Optional((480.0, 46.0)) 當(dāng)前的比例 2.0
//尺寸 Optional((480.0, 46.0)) 當(dāng)前的比例 3.0
10.這個(gè)解釋了7峻村,8
11.切割圖片的時(shí)候麸折,rect的寬高要乘以 屏幕比例
代理更改如下
//3.設(shè)置背景照片
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) 當(dāng)前的比例 \(UIScreen.mainScreen().scale)")
//4.設(shè)置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)) 當(dāng)前的比例 2.0
//尺寸 Optional((480.0, 46.0)) 當(dāng)前的比例 3.0
//5.設(shè)置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方法,更改內(nèi)部image的尺寸
調(diào)整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)
}
6.給btn一個(gè)選中的image的照片(還是按照4那樣切圖)
let selectedSmallImage = UIImage(named: "LuckyAstrologyPressed")
let seletedImageRef = CGImageCreateWithImageInRect(selectedSmallImage?.CGImage, rect)
btn.setImage(UIImage(CGImage: seletedImageRef!), forState: UIControlState.Selected)
7.重寫setHightLighit方法 (但是沒寫明白)
為什么要重寫setHightLighit
這個(gè)方法粘昨?因?yàn)楫?dāng)你按下去的時(shí)候磕谅,出去高亮狀態(tài),如果你重寫了這個(gè)方法雾棺,就不會(huì)有高亮的狀態(tài)膊夹,現(xiàn)在的項(xiàng)目中你按住某個(gè)btn,是黑色的捌浩,有bug
oc中自定義一個(gè)button放刨,內(nèi)部這樣寫,就不會(huì)有高亮了
- (void)setHighlighted:(BOOL)highlighted{
}
swift中我不會(huì)好尷尬尸饺,就是截獲set方法进统,然后不讓父類實(shí)現(xiàn)這個(gè)方法 ,我去監(jiān)聽了set浪听,但是感覺內(nèi)部還是執(zhí)行了super.setHightlighted方法~~有木有知道這個(gè)的同學(xué)螟碎,給我講講吧,謝謝哈
//swift中截獲set方法迹栓,但是我懷疑這里面掉分,已經(jīng)調(diào)用了父類的set方法,
override var highlighted: Bool{
didSet{
}
}
8 UIControlEvents.TouchDown
和UIControlEvents.TouchUpInside
的區(qū)別
這個(gè)想必大家都知道克伊,強(qiáng)者是一按下去酥郭,就執(zhí)行,后者是愿吹,按下去不从,讓后抬起來,在執(zhí)行±绻颍現(xiàn)在想執(zhí)行的效果是--按下去就被選中椿息,就執(zhí)行
func btnBeSelected(btn:UIButton){
//1.先讓之前選中的按鈕取消選中
selectedBtn?.selected = false
//2.讓剛剛點(diǎn)的按鈕設(shè)置成選中狀態(tài)
btn.selected = true
//3.使用全局變量保存剛剛點(diǎn)中的btn
selectedBtn = btn
}
代碼是這樣寫的歹袁,但是效果不是這樣的~不知道咋回事,有時(shí)間再看看
9.為裝盤添加2個(gè)方法寝优,開始和結(jié)束
很簡單宇攻,就是開始和結(jié)束
/**
開始旋轉(zhuǎn)
*/
func startRotating()
{
if (self.link != nil)
{
return
}
//1.生成定時(shí)器
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的區(qū)別和使用情景
為甚使用的是
CADisplayLink
?
這個(gè)方法一秒鐘調(diào)用60次倡勇,可以非吵阉ⅲ快速,而NSTimer
調(diào)用的是1秒的妻熊,不能像前者調(diào)用頻率那么快
這兩個(gè)的使用場景是什么夸浅?
特別快的使用CADisplayLink
,一秒及以后的調(diào)用NSTimer
11.避免多個(gè)定時(shí)器同時(shí)工作,會(huì)出什么問題扔役?
開始的方法中必須調(diào)用這個(gè)
if (self.link != nil)
{
return
}
防止多個(gè)定時(shí)器同事工作帆喇,一調(diào)用開始,先判斷是不是有值亿胸,如果有坯钦,退出,否則多個(gè)在一起侈玄,越來越來轉(zhuǎn)速婉刀,疊加的~
11.細(xì)節(jié),(大黃圓盤)交互yes序仙,no
體現(xiàn)有沒有工作經(jīng)驗(yàn)突颊,可以看看有沒有這個(gè)大黃圓盤的交互,就是當(dāng)我們點(diǎn)擊中間開始按鈕的時(shí)候潘悼,圓盤要快速旋轉(zhuǎn)律秃,但是那時(shí)候是不餓能夠點(diǎn)擊圓盤的任何按鈕的,所以治唤,交互式no棒动,其他事yes
12.點(diǎn)擊開始按鈕
這段代碼有點(diǎn)意思,就是先知道宾添,layer層的動(dòng)畫都是假象船惨,點(diǎn)擊某個(gè)按鈕都是不準(zhǔn)的,但是UIView的動(dòng)畫是真實(shí)的辞槐,可以點(diǎn)擊到具體的那個(gè)Btn的掷漱,但是這里我們就是讓他快速的旋轉(zhuǎn)粘室,所以給個(gè)layer層就好了
這里使用到的是timingFunction榄檬,進(jìn)入緩慢,出來緩慢衔统,我們設(shè)置3次鹿榜,所以我們旋轉(zhuǎn)圈數(shù)和時(shí)間都乘以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")
}
監(jiān)聽了結(jié)束的代理方法,也執(zhí)行了舱殿,但是2秒之后還是不轉(zhuǎn)奥裸,沒搞懂,歡迎指教~
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都是等寬度的沪袭,點(diǎn)擊可能有誤
點(diǎn)擊底部的時(shí)候湾宙,因?yàn)閎tn都是一樣大的,重疊了冈绊,如何解決侠鳄?
實(shí)際上我們可以讓他的底部不能點(diǎn)擊,只能點(diǎn)擊上邊死宣,那么我們就要重寫but的方法伟恶,設(shè)置那些區(qū)域可以點(diǎn)擊,那些不行
/**
尋找合適的view(可以判斷點(diǎn)view的那個(gè)位置是不可以點(diǎn)擊的博秫,那個(gè)是可以點(diǎn)擊的)
:param: point 當(dāng)前點(diǎn)所在位置
:param: event 點(diǎn)擊事件
: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
}
}