第 2 章:設(shè)計(jì) COSMOS

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

3月11日呈驶,我得了一個(gè)機(jī)會(huì),可以寫(xiě)一篇教程竖共,發(fā)表在知名網(wǎng)站上苗胀。所以,我開(kāi)始和 Jake 討論一些能夠設(shè)計(jì)享潜、開(kāi)發(fā)困鸥、發(fā)布的概念,能夠抓住 C4 Swift 版本的要點(diǎn)剑按,學(xué)習(xí)如何使用這個(gè)新系統(tǒng)來(lái)創(chuàng)建動(dòng)畫(huà)...想到:很多基礎(chǔ)的動(dòng)畫(huà)出現(xiàn)疾就,然后組合成一個(gè)優(yōu)雅的界面,我們看了很多 UI 視頻艺蝴,然后頭腦風(fēng)暴猬腰。

Jake 想到了一個(gè)點(diǎn)子:

...中間出現(xiàn)了一個(gè)實(shí)體圓圈,點(diǎn)擊后猜敢,稍微縮小一下(原來(lái)的90%)姑荷,然后從中心接著放射出八個(gè)圓圈盒延,每個(gè)圓圈都比之前的圓圈大一點(diǎn),最外層的圓圈里有不同的圖標(biāo)鼠冕,點(diǎn)擊叉號(hào)關(guān)閉所有的圓圈添寺,回到初始狀態(tài)

于是我們看了很多的視頻。

討論概念懈费。

好像行得通计露。

運(yùn)行程序。

這就是我們的工作方式憎乙。

1. 模擬和測(cè)試

實(shí)際的應(yīng)用比較簡(jiǎn)單票罐,盡管有很多組件,設(shè)計(jì)交互界面寨闹、背景胶坠,可能花費(fèi)一些時(shí)間調(diào)整,同樣的繁堡,正是應(yīng)用還是簡(jiǎn)單沈善,盡管這些調(diào)整比較復(fù)雜,不過(guò)從創(chuàng)建到完成椭蹄,這個(gè)過(guò)程能給我們提供最好的教程素材闻牡。

Jake 展示的設(shè)計(jì)稿只有一個(gè)頁(yè)面,里面有一個(gè)炫酷的動(dòng)畫(huà)菜單绳矩,多層視差背景罩润。我看了一下,思考如何才能讓兩個(gè)組件合并起來(lái)翼馆。

2. 背景

背景部分的工作比較容易分解割以,主要就是很多不同內(nèi)容的圖層在按照不同的速度移動(dòng)。

里面有:

  1. 大星星
  2. 小星星
  3. 連接星星的線
  4. 三個(gè)背景星星層
  5. 兩個(gè)星云層

這完全可以做到应媚,在和 Jake 溝通之后严沥,我寫(xiě)了一個(gè)清單列出我需要他定義的一些東西:

  1. 角度/指示器動(dòng)畫(huà)
  2. 單個(gè)的星座
  3. 三層前景風(fēng)格 + 運(yùn)動(dòng)效果
  4. 三層 星星 背景風(fēng)格 + 運(yùn)動(dòng)效果(incl. # of stars 是什么意思?)
  5. 兩層幸運(yùn)背景風(fēng)格 + 運(yùn)動(dòng)效果

第一步中姜,得到 layer 的數(shù)量消玄,同時(shí)獲取視差角度...需要八個(gè),所以我先用是個(gè)來(lái)測(cè)試一下實(shí)際的效果丢胚。

本章的代碼只是我在真正開(kāi)發(fā)之前的一些測(cè)試展示效果翩瓜,所以當(dāng)你看完這章后,記得刪掉在本章添加的所有代碼携龟。

class WorkSpace: CanvasController {
    //創(chuàng)建一個(gè)空的數(shù)組變量兔跌,用來(lái)添加 layers
    var layers = [UIScrollView]()

    override func setup() {
        //當(dāng) layer 數(shù)量小于 10時(shí),執(zhí)行循環(huán)體里的代碼
        repeat {
            //創(chuàng)建一個(gè) layer峡蟋,它的 frame 值和 canvas 的 frame 值一樣
            let layer = UIScrollView(frame: view.frame)
            //設(shè)置每層 layer 內(nèi)容的大小浮定,高度為 0 相满,防止屏幕垂直滾動(dòng)
            layer.contentSize = CGSizeMake(layer.frame.size.width * 10, 0)
            //把 layer 添加到 canvas 上以及數(shù)組里
            canvas.add(layer)
            layers.append(layer)
        } while layers.count < 10
    }
}

挺簡(jiǎn)單的吧,使用的工程的文件正是前一章中的桦卒,我在 WorkSpace 文件中添加一個(gè) repeat 循環(huán)體,來(lái)創(chuàng)建新的 layer匿又,添加到 canvas 上方灾,直到創(chuàng)建完 10 layer 為止。每創(chuàng)建一個(gè) layer碌更,我都會(huì)把 layer 的 contentSize 設(shè)置的超級(jí)大(文本中裕偿,是 canvas 的二十倍寬)。設(shè)置 contentSize 的高度為 0痛单,這樣就不會(huì)垂直滾動(dòng)了嘿棘。

在這時(shí),如果我運(yùn)行應(yīng)用旭绒,我會(huì)什么都看不到鸟妙,所以我修改一下循環(huán)體里代碼,給每個(gè) layer 增加一個(gè) label 控件挥吵。

class WorkSpace: CanvasController {
    //創(chuàng)建空的數(shù)組變量重父,用來(lái)存儲(chǔ) layer
    var layers = [InfiniteScrollView]()

    override func setup() {
        //當(dāng) layer 數(shù)量小于 10時(shí),執(zhí)行循環(huán)體里的代碼
        repeat {
            //創(chuàng)建一個(gè) layer忽匈,它的 frame 值和 canvas 的 frame 值一樣
            let layer = InfiniteScrollView(frame: view.frame)
            //設(shè)置每層 layer 內(nèi)容的大小房午,高度為 0 ,防止屏幕垂直滾動(dòng)
            layer.contentSize = CGSizeMake(layer.frame.size.width * 10, 0)
            //把 layer 添加到 canvas 上以及數(shù)組里
            canvas.add(layer)
            layers.append(layer)

            //創(chuàng)建一個(gè)中心點(diǎn)變量丹允,用來(lái)定位這些 label
            var center = Point(24,canvas.height/2.0)
            //計(jì)算 layer 的數(shù)量(因?yàn)槲覀円幼詈笠粋€(gè) layer郭厌,從 10 開(kāi)始倒序添加)
            let layerNumber = 10 - layers.count
            //創(chuàng)建字體,字號(hào)是當(dāng)前 layer 的數(shù)量
            let font = Font(name: "AvenirNext-DemiBold", size:Double(layers.count+1) * 8.0)!
            //創(chuàng)建運(yùn)行循環(huán)體知道每個(gè) layer 都有一個(gè) label
            repeat {
                //創(chuàng)建一個(gè) label
                let label = TextShape(text: "\(layerNumber)", font: font)!
                //居中
                label.center = center
                //更新中心點(diǎn)的位置
                center.x += 130.0
                //把 layer 添加到數(shù)組里
                layer.add(label)
            } while center.x < Double(layer.contentSize.width)
        } while layers.count < 10
    }
}

修改原來(lái)的設(shè)置雕蔽,加入一個(gè)內(nèi)嵌的 repeat 循環(huán)體折柠,直到全部 layer 的包含一個(gè) label —— 每個(gè) label 基于所在的 layer 編號(hào)。

現(xiàn)在運(yùn)行程序萎羔,應(yīng)用里會(huì)出現(xiàn) label 控件液走,不過(guò)!如果我滾動(dòng)界面贾陷,只有一個(gè) layer 在滾動(dòng)...

下一步就是創(chuàng)建一個(gè)觀察器缘眶,查看一下最上層的 layer,在滾動(dòng)時(shí)將剩下的 layer 移走髓废。在 setup 的最下方巷懈,添加下列代碼:

if let top = layers.last {
    //創(chuàng)建一個(gè)上下文變量
    var c = 0
    //添加 WorkSpace 作為最上層 layer 的 contentOffset 的觀察者
    top.addObserver(self, forKeyPath: "contentOffset", options: NSKeyValueObservingOptions.New, context: &c)
}

這一步把 WorkSpace 作為最上層 layer 的 contentOffset 的觀察者。現(xiàn)在慌洪,讓代碼更漂亮一些顶燕,我創(chuàng)建一個(gè)函數(shù)凑保,關(guān)聯(lián) layer 的運(yùn)動(dòng)軌跡,改變其他 layer 的軌跡涌攻,如下:

override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
    //遍歷所有的 layer欧引,停在在從上數(shù)第二層的 layer 那里
    for i in 0..<layers.count-1 {
        //獲取當(dāng)前的 layer
        let layer = self.layers[i]
        //基于 layer 的位置創(chuàng)建一個(gè) mod 值(layer 0 = 0.1, layer 1 = 0.2, ...)
        let mod = 0.1 * CGFloat(i+1)
        //獲取最頂層 layer 偏移量的 x 值
        if let x = layers.last?.contentOffset.x {
            //設(shè)置內(nèi)容的偏移量是當(dāng)前 layer * mod
            layer.contentOffset = CGPointMake(x*mod,0)
        }
    }
}

漂亮。現(xiàn)在我們知道這是個(gè) layer 絕對(duì)會(huì)出現(xiàn)了...不過(guò)恳谎,這里怎么會(huì)有一堆的媒體芝此?...測(cè)試一下,Jake 看了一下因痛,每層星星的數(shù)量大約在 15 個(gè)婚苹,還給我一個(gè)小的白星星。

我接著把內(nèi)部 repeat 循環(huán)里的 label 換成圖片鸵膏,如下:

//實(shí)例化中心位置膊升,每個(gè) layer 有 10 * 15 個(gè)星星
let starCount = layers.count * 15
canvas.backgroundColor = black
//循環(huán),直到 starCount
for _ in 0..<starCount {
    //給每個(gè)星星創(chuàng)建一張圖片
    let img = Image("6smallStar")!
    //允許圖片可以適當(dāng)按比例縮放
    img.constrainsProportions = true
    //縮放圖片的寬度
    img.width *= 0.1 * Double(layers.count+1)
    //將中心點(diǎn)設(shè)置為 layer 隨便某個(gè)位置上
    img.center = Point(Double(layer.contentSize.width)*random01(),canvas.height*random01())
    //添加到數(shù)組里
    layer.add(img)
}

運(yùn)行程序谭企,模擬器中應(yīng)用效果如下:

應(yīng)用在 iPhone 5 上運(yùn)行良好廓译,這個(gè)是個(gè)層次運(yùn)行料號(hào),那么剩下的問(wèn)題就是審美的問(wèn)題赞咙,還需要讓界面更好看一些责循。到這時(shí),Jake 基本上制定了背景部分的全部細(xì)節(jié)攀操。

2.1 單個(gè)星座

12個(gè)星座的符合由三部分構(gòu)成:大星星院仿、小星星和線,Jake 用下圖記錄每種星座中星星的位置:

注意觀察大星星和小星星

2.2 三層近景風(fēng)格 + 運(yùn)動(dòng)效果

接下來(lái)定義三層近景 layer 里的星星怎么樣運(yùn)動(dòng)速和。Jake 的想法是有一個(gè)星星移動(dòng)的地方歹垫,所以我們決定使用三層 layer:大星星、小星星颠放、和線排惨。當(dāng)應(yīng)用中出現(xiàn)某個(gè)特定的符號(hào)時(shí),當(dāng)前的星星需要出現(xiàn)在特定符號(hào)的右邊碰凶,接著所有的東西都在快速移動(dòng)暮芭,從一個(gè)星座到另外一個(gè)星座的時(shí)候,出現(xiàn)非常短的線狀動(dòng)畫(huà)欲低。

2.3 三層 星星 背景風(fēng)格 + 運(yùn)動(dòng)效果

接下來(lái)需要定義背景里有多少星星在動(dòng)辕宏,大約是最上面 layer 的 5%、15%砾莱、20%瑞筐。對(duì)每層有多少星星也有一個(gè)大概的猜測(cè)。

2.4 兩層星云層背景風(fēng)格 + 運(yùn)動(dòng)效果

繼續(xù)腊瑟,Jake 定義了星云和光暈的外表以及如何移動(dòng)聚假。這一步甚至比上一步還要簡(jiǎn)單块蚌,因?yàn)楣鈺瀻缀醪欢窃茖哟蠹s是 10% 的速度膘格。

2.5 角度/指示器動(dòng)畫(huà)

最后一個(gè)界面會(huì)在屏幕頂部出現(xiàn)一條豎線峭范,with a longer dash every 20 dashes。接著瘪贱,每個(gè)星座到達(dá)屏幕的中心位置時(shí)虎敦,都會(huì)出現(xiàn)一個(gè)更長(zhǎng)的白線,在星座符號(hào)的下方:

2.6 最后

最后一件事政敢,寫(xiě)一個(gè)清單列出即將要開(kāi)發(fā)的不同的 layer,在他無(wú)限的好意下胚迫,Jake 發(fā)給我下圖:

3. 菜單

菜單看起挺簡(jiǎn)單喷户,實(shí)際上不然。唯一需要我搞懂的就是我們給這些星座符合設(shè)計(jì)什么樣的動(dòng)畫(huà)效果访锻。

Jake 想用這些符號(hào)作為星座的基本外形

實(shí)際上褪尝,給它們添加動(dòng)畫(huà)效果這事簡(jiǎn)單,難的地方在于創(chuàng)造它們期犬,因?yàn)槲覀兿M鼈冇凶约旱呢惾麪柭窂胶友疲瑒?chuàng)建的過(guò)程確實(shí)痛苦的,因?yàn)槲覀儾恢浪麄兊穆窂近c(diǎn)龟虎,像是 IllUstrator 這樣的軟件也不給我們權(quán)限獲取數(shù)據(jù)璃谨,還有,我不想寫(xiě)一個(gè) SVG 導(dǎo)出器鲤妥,那也太多余了佳吞。

那么,我們?cè)撛趺崔k呢棉安?

使用 PaintCode 畫(huà)出外形底扳,接著添加曲線軌跡,保存到 Core Graphics 代碼里贡耽,如下:

UIBezierPath* bezier2Path = UIBezierPath.bezierPath;
[bezier2Path moveToPoint: CGPointMake(250, 200)];
[bezier2Path addLineToPoint: CGPointMake(150, 200)];
[bezier2Path addCurveToPoint: CGPointMake(100, 150) controlPoint1: CGPointMake(122.4, 200) controlPoint2: CGPointMake(100, 177.6)];
...
[bezier2Path closePath];

當(dāng)我把代碼換成下面這樣后:

let bezier = Path()

bezier.moveToPoint(Point(250,200))
bezier.addLineToPoint(Point(150,200))
bezier.addCurveToPoint(Point(100,150), control1:Point(122.4,200), control2:Point(100,177.6))
...

事情開(kāi)始變得更清晰衷模,更容易處理了。現(xiàn)在我還需要得到星座符號(hào)的外形添加到 C4 代碼里蒲赂,無(wú)需費(fèi)太多力就能實(shí)現(xiàn)我們想要實(shí)現(xiàn)的效果阱冶。

比如,讓 shape 的外形完全和要求的一樣:

shape.strokeEnd = 1.0

3.1 紅線

走到這一步凳宙,我準(zhǔn)備創(chuàng)建菜單了熙揍,因此需要下面的紅線,標(biāo)注菜單上所有元素的具體的位置氏涩、尺寸等等届囚。

Jake 的工作做的真棒有梆,給我準(zhǔn)備了這張圖:

4. 該進(jìn)行下一章了

基本的視覺(jué)概念都解釋了,
現(xiàn)在該做一些實(shí)際的開(kāi)發(fā)工作了意系。不過(guò)泥耀,在職之前,我總結(jié)了一些必須要表明的問(wèn)題:

  1. 定義外形 - 我會(huì)復(fù)用很多外形蛔添,也會(huì)給它們添加動(dòng)畫(huà)效果痰催,我會(huì)用自定義的貝塞爾曲線路徑,而不是單單導(dǎo)入圖片資源迎瞧。
  2. 復(fù)雜的動(dòng)畫(huà)序列 - 會(huì)有非常復(fù)雜的動(dòng)畫(huà)序列和調(diào)速夸溶,直到得到正確的菜單外展內(nèi)收的效果。
  3. 定義手勢(shì)交互 - 我想讓手勢(shì)交互越簡(jiǎn)單約好凶硅,當(dāng)然了還要獨(dú)一無(wú)二缝裁。
  4. 視差 + 無(wú)限滾動(dòng)視圖 - 必須給應(yīng)用增加視差,我需要非常小心處理足绅,開(kāi)發(fā)完成后捷绑,應(yīng)用的性能表現(xiàn)要非常高才行。

記得刪除掉 WorkSpace.swift 文件里的測(cè)試代碼...只有一個(gè)空的 setup() 方法氢妈。

繼續(xù)下一章粹污!

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末壮吩,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蕾总,更是在濱河造成了極大的恐慌粥航,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,888評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件生百,死亡現(xiàn)場(chǎng)離奇詭異递雀,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)蚀浆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門缀程,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人市俊,你說(shuō)我怎么就攤上這事杨凑。” “怎么了摆昧?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,386評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵撩满,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng)伺帘,這世上最難降的妖魔是什么昭躺? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,726評(píng)論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮伪嫁,結(jié)果婚禮上领炫,老公的妹妹穿的比我還像新娘。我一直安慰自己张咳,他們只是感情好帝洪,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,729評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著脚猾,像睡著了一般葱峡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上龙助,一...
    開(kāi)封第一講書(shū)人閱讀 52,337評(píng)論 1 310
  • 那天族沃,我揣著相機(jī)與錄音,去河邊找鬼泌参。 笑死,一個(gè)胖子當(dāng)著我的面吹牛常空,可吹牛的內(nèi)容都是我干的沽一。 我是一名探鬼主播,決...
    沈念sama閱讀 40,902評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼漓糙,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼铣缠!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起昆禽,我...
    開(kāi)封第一講書(shū)人閱讀 39,807評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蝗蛙,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后醉鳖,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體捡硅,經(jīng)...
    沈念sama閱讀 46,349評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,439評(píng)論 3 340
  • 正文 我和宋清朗相戀三年盗棵,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了壮韭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,567評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡纹因,死狀恐怖喷屋,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情瞭恰,我是刑警寧澤屯曹,帶...
    沈念sama閱讀 36,242評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響恶耽,放射性物質(zhì)發(fā)生泄漏密任。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,933評(píng)論 3 334
  • 文/蒙蒙 一驳棱、第九天 我趴在偏房一處隱蔽的房頂上張望批什。 院中可真熱鬧,春花似錦社搅、人聲如沸驻债。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,420評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)合呐。三九已至,卻和暖如春笙以,著一層夾襖步出監(jiān)牢的瞬間淌实,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,531評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工猖腕, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拆祈,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,995評(píng)論 3 377
  • 正文 我出身青樓倘感,卻偏偏與公主長(zhǎng)得像放坏,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子老玛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,585評(píng)論 2 359

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,300評(píng)論 25 707
  • 在iOS中隨處都可以看到絢麗的動(dòng)畫(huà)效果淤年,實(shí)現(xiàn)這些動(dòng)畫(huà)的過(guò)程并不復(fù)雜,今天將帶大家一窺iOS動(dòng)畫(huà)全貌蜡豹。在這里你可以看...
    F麥子閱讀 5,115評(píng)論 5 13
  • 在iOS中隨處都可以看到絢麗的動(dòng)畫(huà)效果麸粮,實(shí)現(xiàn)這些動(dòng)畫(huà)的過(guò)程并不復(fù)雜,今天將帶大家一窺ios動(dòng)畫(huà)全貌镜廉。在這里你可以看...
    每天刷兩次牙閱讀 8,514評(píng)論 6 30
  • 沿落基山脈一路前行弄诲,四季在侵尋,群山在變藍(lán)娇唯,湖光化作汽車窗前的霧氣…… 我們將自己放進(jìn)這無(wú)字的天地里威根,奔赴一場(chǎng)人與...
    繁華舊夢(mèng)閱讀 2,801評(píng)論 16 10
  • 王曉梅,休息是為了更好的上路视乐。
    沙漏記得閱讀 132評(píng)論 0 0