第 18 章:圖標(biāo)菜單

原文鏈接
作者:C4 開源項(xiàng)目
譯者:Crystal Sun
全部章節(jié)請(qǐng)關(guān)注此文集C4教程翻譯
校對(duì)后的內(nèi)容請(qǐng)看這里

接下來港粱,想把圖標(biāo)添加到菜單里坎拐,然后實(shí)現(xiàn)動(dòng)畫效果束铭。圖標(biāo)的設(shè)計(jì)理念非常直接:一開始像是一個(gè)點(diǎn)炭分,慢慢變大蒋院,移動(dòng)到最終的位置亏钩,出現(xiàn)全部的形狀,如下圖:

1. 定位

這部分有技巧的地方不是動(dòng)畫和移動(dòng)效果欺旧,這些都簡(jiǎn)單姑丑,而是每個(gè)姓朱的位置,這樣才能一開始是個(gè)點(diǎn)辞友,慢慢轉(zhuǎn)變成全部的形狀秕磷。有一些方法可以實(shí)現(xiàn)這種效果:

  1. 創(chuàng)建一個(gè)點(diǎn)和一個(gè)形狀(shape)卸夕,一直隱藏形狀知道點(diǎn)移動(dòng)到自己的位置,隱藏點(diǎn),顯示形狀敌呈。
  2. 創(chuàng)建一個(gè)點(diǎn)诈闺,讓這個(gè)點(diǎn)成為形狀的一部分煤辨,移動(dòng)點(diǎn)到某個(gè)位置竞滓,然后把點(diǎn)轉(zhuǎn)變成完全的形狀。
  3. 有一個(gè)點(diǎn)疫向,但不是真的點(diǎn)咳蔚,只是看起來像是一個(gè)點(diǎn),設(shè)置形狀的 strokeEnd 在右邊搔驼,形狀的 lineCap.Round屹篓。

我們使用的是第三種方法 strokeEnd 來管理圖標(biāo)的動(dòng)畫。然而匙奴,問題也出現(xiàn)了堆巧,就是每個(gè)形狀的“初始”點(diǎn)各不同 —— 我們需要知道每個(gè)形狀,在點(diǎn)狀態(tài)下或者在全部出現(xiàn)狀態(tài)下的偏移量

下面是摩羯座的圖標(biāo)泼菌,開始點(diǎn)已經(jīng)高亮標(biāo)注出了:

開始點(diǎn)的位置是 {30.0,12.2}谍肤,對(duì)應(yīng)的 frame 是 {0.750,0.387}

創(chuàng)建一個(gè)“點(diǎn)”的效果哗伯,我們只需要給每個(gè)圖標(biāo)設(shè)置:

shape.strokeEnd = 0.001
shape.lineCap = .Round

如果我們用圖標(biāo)的中心點(diǎn)定位荒揣,結(jié)束和開始狀態(tài)下的菜單看起來會(huì)是下圖這樣:

如果我們使用第一個(gè)點(diǎn)作為形狀的 anchorPoint,我們會(huì)得到下面這樣的效果:

我們的想法是焊刹,在結(jié)束狀態(tài)下使用 anchorPoint系任,另外一個(gè)狀態(tài)使用 center恳蹲。不過,如果我們不停的轉(zhuǎn)換位置俩滥,實(shí)現(xiàn)起來會(huì)很復(fù)雜嘉蕾。所以我們必須使用 anchorPoint 計(jì)算實(shí)際的 center,使用這個(gè) center 在不同的狀態(tài)下來?yè)Q轉(zhuǎn)換霜旧。

開始行動(dòng)吧错忱。

2. 獲取圖標(biāo)

第一步就是從符號(hào)庫(kù)里獲取圖標(biāo),更新圖標(biāo)的錨點(diǎn)挂据。

打開 MenuIcons.swift 文件以清,在類里添加下列代碼:

func taurus() -> Shape {
    let shape = AstrologicalSignProvider.sharedInstance.taurus().shape
  shape.anchorPoint = Point()
  return shape
}

func aries() -> Shape {
  let shape = AstrologicalSignProvider.sharedInstance.aries().shape
  shape.anchorPoint = Point(0.0777,0.536)
  return shape
}

func gemini() -> Shape {
  let shape = AstrologicalSignProvider.sharedInstance.gemini().shape
  shape.anchorPoint = Point(0.996,0.0)
  return shape
}

func cancer() -> Shape {
  let shape = AstrologicalSignProvider.sharedInstance.cancer().shape
  shape.anchorPoint = Point(0.0,0.275)
  return shape
}

func leo() -> Shape {
  let shape = AstrologicalSignProvider.sharedInstance.leo().shape
  shape.anchorPoint = Point(0.379,0.636)
  return shape
}

func virgo() -> Shape {
  let shape = AstrologicalSignProvider.sharedInstance.virgo().shape
  shape.anchorPoint = Point(0.750,0.387)
  return shape
}

func libra() -> Shape {
  let shape = AstrologicalSignProvider.sharedInstance.libra().shape
  shape.anchorPoint = Point(1.00,0.559)
  return shape
}

func pisces() -> Shape {
  let shape = AstrologicalSignProvider.sharedInstance.pisces().shape
  shape.anchorPoint = Point(0.099,0.004)
  return shape
}

func aquarius() -> Shape {
  let shape = AstrologicalSignProvider.sharedInstance.aquarius().shape
  shape.anchorPoint = Point(0.0,0.263)
  return shape
}

func sagittarius() -> Shape {
  let shape = AstrologicalSignProvider.sharedInstance.sagittarius().shape
  shape.anchorPoint = Point(1.0,0.349)
  return shape
}

func capricorn() -> Shape {
  let shape = AstrologicalSignProvider.sharedInstance.capricorn().shape
  shape.anchorPoint = Point(0.288,0.663)
  return shape
}

func scorpio() -> Shape {
  let shape = AstrologicalSignProvider.sharedInstance.scorpio().shape
  shape.anchorPoint = Point(0.255,0.775)
  return shape
} 

每個(gè)方法都從符號(hào)庫(kù)里獲取了圖標(biāo),設(shè)置錨點(diǎn)為圖標(biāo)的開始點(diǎn)崎逃。我們不需要知道符號(hào)的其他信息了(比如:大/小 點(diǎn)和線等等)掷倔。所以我們只需返回修改過錨點(diǎn)的形狀。

接下來个绍,我們還想能存儲(chǔ)使用的符號(hào)的副本勒葱,之后我們會(huì)對(duì)這些符號(hào)進(jìn)行操作。所以障贸,創(chuàng)建一個(gè)形狀詞典變量來存儲(chǔ)符號(hào)和方法,這些方法會(huì)給符號(hào)創(chuàng)建格子的風(fēng)格:

var signIcons : [String:Shape]!

接著吟宦,把下列方法添加到類里:

func createSignIcons() {
    signIcons = [String:Shape]()
    signIcons["aries"] = aries()
    signIcons["taurus"] = taurus()
    signIcons["gemini"] = gemini()
    signIcons["cancer"] = cancer()
    signIcons["leo"] = leo()
    signIcons["virgo"] = virgo()
    signIcons["libra"] = libra()
    signIcons["scorpio"] = scorpio()
    signIcons["sagittarius"] = sagittarius()
    signIcons["capricorn"] = capricorn()
    signIcons["aquarius"] = aquarius()
    signIcons["pisces"] = pisces()
    
    for shape in [Shape](self.signIcons.values) {
        shape.strokeEnd = 0.001 //in combination with the next two settings
        shape.lineCap = .Round  //strokeEnd 0.001 makes a round dot at
        shape.lineJoin = .Round //the beginning of the shape's path
        
        shape.transform = Transform.makeScale(0.64, 0.64, 1.0)
        shape.lineWidth = 2
        shape.strokeColor = white
        shape.fillColor = clear
    }
}

我們從庫(kù)里拿出來的符號(hào)還是原始符號(hào)篮洁,我們需要縮小一下,如果我們不進(jìn)行這行操作:hape.transform = Transform.makeScale(0.64, 0.64, 1.0)殃姓,符號(hào)的形狀就會(huì)超級(jí)大袁波,如下圖:

3. 目標(biāo)位置

接下來,我們需要計(jì)算每個(gè)形狀的目標(biāo)位置蜗侈,存儲(chǔ)到兩個(gè)數(shù)組中篷牌,在類里添加下面的代碼:

var innerTargets : [Point]!
var outerTargets : [Point]!

接著添加下列方法:

func positionSignIcons() {
    innerTargets = [Point]()
    let provider = AstrologicalSignProvider.sharedInstance
    let r = 10.5
    let dx = canvas.center.x
    let dy = canvas.center.y
    for i in 0..<provider.order.count {
        let ? = M_PI/6 * Double(i)
        let name = provider.order[i]
        if let sign = signIcons[name] {
            sign.center = Point(r * cos(?) + dx, r * sin(?) + dy)
            canvas.add(sign)
            sign.anchorPoint = Point(0.5,0.5)
            innerTargets.append(sign.center)
        }
    }
    
    outerTargets = [Point]()
    for i in 0..<provider.order.count {
        let r = 129.0
        let ? = M_PI/6 * Double(i) + M_PI/12.0
        outerTargets.append(Point(r * cos(?) + dx, r * sin(?) + dy))
    }
}

上述過程使用了以下概念:

  1. 創(chuàng)建形狀,通過 anchorPoint 方法來給每個(gè)形狀設(shè)置起始點(diǎn)
  2. 調(diào)用 shape.center 實(shí)際上返回的是形狀的位置 anchorPoint踏幻,相對(duì)于每個(gè)形狀的 superview
  3. 調(diào)整 anchorPoint 到形狀的中心(例如 {0.5,0.5})枷颊,不會(huì)改變形狀的位置
  4. 重置 anchorPoint 后,我們能夠獲取形狀實(shí)際的中心位置该面,作為目標(biāo)

最后夭苗,在 createSignIcons 的最后,給所有的符號(hào)都設(shè)置了風(fēng)格后隔缀,添加下列代碼:

positionSignIcons()

現(xiàn)在题造,讓我們看一下如何將這些排列在一起。

3.1 檢查一下

將菜單的 backgroundColor 值改成 C4Purple猾瘸,創(chuàng)建符號(hào)圖標(biāo)界赔,setup() 方法應(yīng)該像下面這樣:

public override func setup() {
    canvas.backgroundColor = COSMOSbkgd
    createSignIcons()
}

WorkSpace 里丢习,更新 setup() 方法,如下:

override func setup() {
    canvas.add(MenuIcons().canvas)
}

運(yùn)行程序淮悼,效果如下:

4 圖標(biāo)的動(dòng)畫效果

有四個(gè)不同的動(dòng)畫需要?jiǎng)?chuàng)建:位置的出/入咐低,形狀的出現(xiàn)/隱藏。所以敛惊,創(chuàng)建四個(gè)動(dòng)畫變量:

var signIconsOut : ViewAnimation!
var signIconsIn : ViewAnimation!
var revealSignIcons : ViewAnimation!
var hideSignIcons : ViewAnimation!

增加下列方法:

func createSignIconAnimations() {
}

需要四個(gè)動(dòng)畫是由于設(shè)計(jì)原因渊鞋,的吧是線在出入時(shí)不同的方向有不同的順序。

出:移動(dòng)瞧挤,出現(xiàn)锡宋;入:隱藏,移動(dòng)特恬。

由于模式正好相反执俩,當(dāng)動(dòng)畫開始后,我們需要獲取單獨(dú)的動(dòng)畫來應(yīng)對(duì)移動(dòng)和隱藏癌刽,形成序列役首。在方法里添加如下內(nèi)容:

revealSignIcons = ViewAnimation(duration: 0.5) {
    for sign in [Shape](self.signIcons.values) {
        sign.strokeEnd = 1.0
    }
}
revealSignIcons?.curve = .EaseOut

hideSignIcons = ViewAnimation(duration: 0.5) {
    for sign in [Shape](self.signIcons.values) {
        sign.strokeEnd = 0.001
    }
}
hideSignIcons?.curve = .EaseOut

需要標(biāo)注一下:

  1. 將每個(gè)動(dòng)畫分開,更容易在每一步進(jìn)行自定義
  2. 能夠明確每個(gè)方向的持續(xù)時(shí)間
  3. 把 stroke 設(shè)置回 0.001显拜,保存點(diǎn)

現(xiàn)在衡奥,把下列代碼添加到 createSignIconAnimations

signIconsOut = ViewAnimation(duration: 0.33) {
    for i in 0..<AstrologicalSignProvider.sharedInstance.order.count {
        let name = AstrologicalSignProvider.sharedInstance.order[i]
        if let sign = self.signIcons[name] {
            sign.center = self.outerTargets[i]
        }
    }
}
signIconsOut?.curve = .EaseOut

//把圖標(biāo)移動(dòng)到結(jié)束位置
signIconsIn = ViewAnimation(duration: 0.33) {
    for i in 0..<AstrologicalSignProvider.sharedInstance.order.count {
        let name = AstrologicalSignProvider.sharedInstance.order[i]
        if let sign = self.signIcons[name] {
            sign.center = self.innerTargets[i]
        }
    }
}
signIconsIn?.curve = .EaseOut

我們?cè)谶@里獲取了圖標(biāo),設(shè)置圖標(biāo)在內(nèi)環(huán)時(shí)的中心點(diǎn)和在外環(huán)時(shí)的中心點(diǎn)远荠。

4.1 查看一下效果

調(diào)用出現(xiàn)和移動(dòng)動(dòng)畫來測(cè)試一下矮固,如下:

func animOut() {
    delay(1.0) {
        self.signIconsOut?.animate()
    }

    delay(1.5) {
        self.revealSignIcons?.animate()
    }
    
    delay(2.5) {
        self.animIn()
    }
}

func animIn() {
    delay(0.25) {
        self.hideSignIcons?.animate()
    }

    delay(1.0) {
        self.signIconsIn?.animate()
    }

    delay(2.5) {
        self.animOut()
    }
}

setup() 里調(diào)用 animOut(),如下:

public override func setup() {
    canvas.backgroundColor = COSMOSbkgd
    createSignIcons()
    createSignIconAnimations()
    animOut()
}

效果如下:

5. 打掃一下

刪除 animInanimOut 方法譬淳,修改 setup() 方法如下:

public override func setup() {
    canvas.frame = Rect(0,0,80,80)
    canvas.backgroundColor = clear
    createSignIcons()
    createSignIconAnimations()
}

獲取 MenuIcons.swift.

干凈利索档址!

本文由 SwiftGG 翻譯組翻譯,已經(jīng)獲得作者翻譯授權(quán)邻梆。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末守伸,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子浦妄,更是在濱河造成了極大的恐慌尼摹,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件剂娄,死亡現(xiàn)場(chǎng)離奇詭異窘问,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)宜咒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門惠赫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人故黑,你說我怎么就攤上這事儿咱⊥タ常” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵混埠,是天一觀的道長(zhǎng)怠缸。 經(jīng)常有香客問我,道長(zhǎng)钳宪,這世上最難降的妖魔是什么揭北? 我笑而不...
    開封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮吏颖,結(jié)果婚禮上搔体,老公的妹妹穿的比我還像新娘。我一直安慰自己半醉,他們只是感情好疚俱,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著缩多,像睡著了一般呆奕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上衬吆,一...
    開封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天梁钾,我揣著相機(jī)與錄音,去河邊找鬼逊抡。 笑死姆泻,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的秦忿。 我是一名探鬼主播麦射,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼蛾娶,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼灯谣!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蛔琅,我...
    開封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤胎许,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后罗售,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辜窑,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年寨躁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了穆碎。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡职恳,死狀恐怖所禀,靈堂內(nèi)的尸體忽然破棺而出方面,到底是詐尸還是另有隱情,我是刑警寧澤色徘,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布恭金,位于F島的核電站,受9級(jí)特大地震影響褂策,放射性物質(zhì)發(fā)生泄漏横腿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一斤寂、第九天 我趴在偏房一處隱蔽的房頂上張望耿焊。 院中可真熱鬧,春花似錦扬蕊、人聲如沸搀别。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽歇父。三九已至,卻和暖如春再愈,著一層夾襖步出監(jiān)牢的瞬間榜苫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工翎冲, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留垂睬,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓抗悍,卻偏偏與公主長(zhǎng)得像驹饺,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子缴渊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,510評(píng)論 25 707
  • 在iOS中隨處都可以看到絢麗的動(dòng)畫效果赏壹,實(shí)現(xiàn)這些動(dòng)畫的過程并不復(fù)雜,今天將帶大家一窺ios動(dòng)畫全貌衔沼。在這里你可以看...
    每天刷兩次牙閱讀 8,465評(píng)論 6 30
  • 在iOS中隨處都可以看到絢麗的動(dòng)畫效果蝌借,實(shí)現(xiàn)這些動(dòng)畫的過程并不復(fù)雜,今天將帶大家一窺iOS動(dòng)畫全貌指蚁。在這里你可以看...
    F麥子閱讀 5,094評(píng)論 5 13
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)菩佑、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,024評(píng)論 4 62
  • 即使不見面凝化,不說話稍坯,不發(fā)信息,心里總會(huì)留一個(gè)位置搓劫,安安穩(wěn)穩(wěn)的放著一個(gè)人瞧哟。一句話袜蚕,一件事,時(shí)間或長(zhǎng)或短的變數(shù)绢涡,但有的...
    何無所求閱讀 403評(píng)論 0 0