我的 WWDC 2019 Scholarship & 來一次完整的使用 Playground(二)

PJHubs發表於2019-03-24

原文地址:PJ 的 iOS 開發日常

這篇文章將會在保證主體內容的完整上加入參加我本科時代最後一個比賽 Apple WWDC 2019 Scholarship 專案的完整講解。

前言

在上週五(15號)時,我刷著微博,突然間看到了樑傑大大轉發 AplloZhu 的一條關於 Apple 今年 WWDC Scholarship 的活動介紹。我注意看了下時間,15 號早上 8 點到 25 號早上 8 點,總共就 10 天的時間,在這個十天的時間使用 Playground 做出一個 demo。當然這裡說的 demo 肯定不是我們平常做技術驗證那般無所謂,要求是能夠完整表達自己的創意,並結合 Apple 的相關 API 完成。

大概是早上 10 點左右看到的訊息,從上週五直到昨天,我整個人都處在一種十分的焦慮過程中,這種焦慮伴隨著激動和不安,幾乎每天晚上都沒有睡好,早上到公司時也沒有任何的狀態,一心想著我要怎麼做好這個事情。

得知這個訊息後,立馬開始在腦海裡搜尋創意,我非常明白 Apple 對創意十分看重,然後又想到這兩年 Apple 對人工智慧的關注幾乎到了 all in 地步,於是結合 CoreML 和 AR 能做些什麼呢......

思考了二十分鐘後,我徹底放棄了通過技術來搜尋創意的想法。突然靈光一閃,想到 Apple 近年來對教育和環境領域是相當重視!如果我做一個跟環境保護有關的教育專案呢?在思考的同時我又瀏覽了一遍 WWDC 2019 Scolarship 的 repo list,把幾乎所有專案介紹中的視訊都看了一遍,總結出了以下幾點:

  • 不需要做太精美的 demo,但精美有加分;
  • 音樂如果運用不當還是算了吧;
  • 重點在於你要表達什麼,而不是你做了什麼;
  • Playground 也可以沒有“互動體驗”,直接看執行;

這時,我懸著的心終於可以放了下來,原來並不一定要做成向 Swift Playground 中那麼精美的 demo 啊!但是值得注意的是,在去年 accepted 的 repo 中,有一部分是視覺上贏了。我在想,如果我直接拼 CoreML 和 AR 等的東西實在是沒發現有什麼好的點子,為何不來一個彎道超車?我也來做一個視覺上的衝擊?

我繼續在大腦裡搜尋,突然!我發現了這麼**“黎錦”**這個東西一直給了我很大的震撼!它不但具備對稱美、粗曠的線條,甚至還有誇張的圖案!在吃午飯的時候我仔仔細細的全盤推演了一遍互動,如果我能夠運用得好“對稱美”這個關鍵點,一定很贊!經過了一番修正後,決定就是這個題目了!

黎錦

準備工作

腦暴

午飯回來後,我開始清空大腦,放下所有其它事情,準備全力以赴。我首先確定了自己要做的是一個具備“對稱美”的 demo,必須要圍繞“對稱”這個事情來展開;而且還要突出黎錦最核心的地方——粗曠的線條和誇張的圖案,頭腦風暴開始了......

健身完後,腦子連同身體一塊輕鬆了,開始構思具體的互動和細節。黎錦,它的本質是紡織品,其次是黎族人對自然的崇拜的表達,最後才是工藝品,所以我最終目的也出來了:

  • 利用紡織品的底紋;
  • 突出黎族人的對自然崇拜的元素;
  • 儘可能做的精美;
  • 利用拼圖的特性。

又經過了一個多小時的時間,把一些細節的地方都完善好,並確定自己也被自己陶醉了後,開始畫原型圖,下面是腦暴時的部分手稿:

手稿

原型圖

臨近下午六點時,終於把一些確定的元素都完成了。不得不說這些“突出黎族人對自然崇拜的元素”實在難以搞定,單是用 Sketch 畫這幾個小圖,一兩個小時就這麼過去了,下面是原型圖的展示:

原型圖 1

原型圖 2

插一句題外話:在此我要強烈鄙視曾經的我。以往參加各種比賽時,我都對原型圖嗤之以鼻,認為這部分工作毫無意義,存粹是浪費時間。但經過 BonfirePLook 這兩個專案後,給我打了一個狠狠的臉!真誠的希望大家在日後進行專案的開發工作時一定要先做好原型圖。

開發

第二天

整個 demo 的核心難點在今天基本上就完成了。主要是解決“對稱”問題。當時我給自己下的一個死要求——不要考慮效能,原因很簡單,第一是沒有時間,第二是我的執行平臺為 Mac,不需要糾結這方面的問題。

鏡面對稱

當使用者在檢視虛線的左半部分拖動色塊時,與之對應在螢幕右半部分的色塊也會隨著一起滑動至同等位置,且為鏡面對稱。這部分功能的實現比較粗暴,但實際上就是這麼一回事,但在記憶體佔用上是有相當大可以進行優化的空間,因為使用者永遠只能操縱左半部分的色塊,而對於右半部分的色塊是無法操縱的,所以位於右半部分的色塊完全可以拋去 UIResponder 等協議的遵循,僅僅只需要 CALayer 即可。

底部欄

這部分比較簡單,一個 UICollectionView 即可完事,但考慮到後期元件重用問題,還是給 UICollectionView 包了一層 UIView。到現在反過頭去看,實際上是沒有任何必要的。

完工圖

第一天完工圖

第三天

今天是週日,時間比較多,開始做一些互動上的東西。

從底部欄拖拽元素至畫布上

首先是從“底部欄”拖拽元素至畫布上。這部分其實也還 OK,需要維護好兩套座標系的轉換,使用者觸控點從底部欄到畫布上這一過程色塊需要進行轉換的座標不能使用底部欄的座標進行,因為色塊在底部欄上的 x 和 y 都是 10 以內的數,如果還以底部欄作為座標轉換的依據,那麼當使用者拖拽色塊時,色塊會直接跑到螢幕的最上方。

因此我們需要以底部欄的 superview 來作為座標轉換的依據,也就是 UIViewControllerview。經過一番調整後,使用了長按手勢來激發拖拽功能,並增加了底部欄元素被拖拽後資料來源的刪除。

自動吸附

這部分 bug 比較多,直到昨天都還在維護相關邏輯。我想要達到的效果是,把畫布分為 smallnormalbig 三個尺寸的大小,其中 normal 是預設大小,3 * 9 的方格充滿畫布。當使用者把元素拖拽到畫布上 touchEnded 時,如果此時元素不滿足完全嵌入離它最近的方格時,系統將自動把該元素“吸附”到距離該元素最近的方格中,下面這個動圖可以比較好的進行展示:

吸附功能

當時之所以想到這個吸附功能,主要是我在玩的過程中沒法判贏~如果只是讓使用者去自己根據完成圖的提示來進行拼圖,那實在是無趣了點,要稍微的營造出一點慢慢的看出端倪,最後拼圖完成後發出一聲“哦!”的感嘆就滿足了~

佔位還原

這個功能是依賴“自動吸附”的。如果使用者此時拖拽了一個元素覆蓋到了一箇舊元素上,系統要自動把拖拽的元素還原到原來的位置上。這部分也 OK~

第四天

今天是週一,距離提交截止還有七天。

判贏

完成了之前把畫布切割成不同 size 的方塊功能後,此時再來思考如果才能算贏就很簡單了,因為畫布本身就是一個二維列表,嗯,就是這麼簡單了。

在 Playground 中跑起來

之前有幾次嘗試使用 Playground 進行開發的糟糕體驗後,這次徹底放棄了使用在 Playground 開發的想法,先用寫一個 app 的架構模式去完成然後再遷移到 Playground 中。

第一次遷移真是把我搞得不行~整了好久根本沒跑起來。幸虧有了去年的 WWDC Scholarship repo list,看了好幾個 repo 後才明白是怎麼個事情,第一次使用 Playground 執行專案可以參考我的這一篇文章

第四天完工圖

第五天

到了今天核心功能基本上都已經完成,接下來要做的就是提升互動,儘可能的做得更加精美,完善除了主流程之外的其它 case 下產生的問題。

畫布大小

這部分的功能在第二天的時候已經思考過了,提供了三種大小的畫布尺寸,就不展開了~

其它

今天糾結錯了一個點。原本想在自定義模式下提供“長方形”、“正方形”和“圓形”三種形狀的畫布,但卻因為最初的架構問題導致一直沒法完成,看著時間越來越短,拼圖模組卻一點沒做~內心開始有些緊張了。今天反反覆覆的把時間浪費了在了修改畫布形狀的功能上。

進過反覆修改後,終於確定了“大力神”的定稿!我當時看到“大力神”的表情......?

黎族守護神之一“大力神”

第六天

今天是週三,留校與畢設導師見面的日子,又多了完整的一天。給自己下了一個必須完成的任務,今天務必要完成拼圖模組。

拼圖模組

拼圖大家都玩過,按照提示圖把零散的部件拼好。我並沒有全盤借用拼圖的全套遊戲模式,而是僅僅採用了它的遊戲邏輯。可以確定的是,肯定不能把畫十幾二十個拼圖元素小圖,這樣不但會把人畫瘋而且時間會浪費得更多,再加上如果考慮到螢幕適配問題那就更痛苦了呢~

於是我想到使用 Core Graphics 的方法對一張圖片按照所需尺寸進行切割,具體實現如下:

extension UIImage {
    /// 通過原圖獲取 rect 大小的圖片
    func image(with rect: CGRect) -> UIImage {
        let scale = UIScreen.main.scale
        let x = rect.origin.x * scale
        let y = rect.origin.y * scale
        let w = rect.size.width * scale
        let h = rect.size.height * scale
        let finalRect = CGRect(x: x, y: y, width: w, height: h)
        
        let originImageRef = self.cgImage
        let finanImageRef = originImageRef!.cropping(to: finalRect)
        let finanImage = UIImage(cgImage: finanImageRef!, scale: scale, orientation: .up)

        return finanImage
    }
}
複製程式碼

通過以上方法,我就達到了只需要通過一些簡單的數學計算就可以根據使用者當前設定 sizeType 型別來控制單個拼圖元素的大小~

拼圖完成

如果使用者都按照拼圖的實際位置放置好了,實際上就是贏了~但當我寫完這部分的邏輯後,看到最終的成果圖,總感覺哪裡不對勁。

奇怪的拼圖完成圖

對比了以後發現!woc???怎麼兩個頭?反覆檢視取拼圖元素的邏輯程式碼後,發現原來是這麼一回事......

我在程式碼中寫的是從每行 x=0 處開始往後取 item=62.5 寬度的圖,連續取三個,但 iPhone 7 的螢幕寬度為 375,一半就是 187.5,三個 62.5 就是 187.5 這沒啥問題,但問題出在我的圖是不是標準的 750 * 1334,而是 647 * 1159,所以這就導致會多往後擷取的問題。不想改圖稍微改動了下相關位置邏輯完事。(其實應該把圖改了)

給拼圖完成後加了一點彩紙動效和背景音樂,但背景音樂不知為何在 Playground 中只“滴”的一下就沒有下文了,彩紙動效如下:

彩紙動效

第七天

今天一直在維護拼圖邏輯,想著趕緊把拼圖做完,然後立馬開始做自定義部分。

第八天

今天是週五,學校下午開了個年級會,又多了完整的一天。今天一定要完成自定義模組,然後明天開始寫各種文案。

自定義模組

完成了拼圖部分後,自定義就很簡單了,剔除判贏、自動吸附和裁切元素三個大頭,加上一些好看的元素替換到底部欄中的拼圖部分中的元素即可。但因為是使用者自定義部分,要提供一定的個性化功能,因為今天已經週五了,好多文案也沒寫,想著那就完成“刪除”和“旋轉”兩個功能好了~

  • 刪除。再次使用長按手勢完成,稍微囉嗦一點的地方在於各種資料來源的刪除和恢復的維護邏輯。
  • 旋轉。使用了雙擊手勢。考慮到使用者在 Playground 中使用旋轉手勢實在是難以操作而做出選擇。在實現旋轉的過程中,也要時刻保持旋轉後的鏡面對稱,不能只是簡單的 currentItem.transform = copyItem.transform 這般簡單了。需要根據不同的情況做幾個取反操作再賦值。

第九天

到了今天不知為何毫無動力,已經沒有再繼續開發和優化下去的慾望了,只想著快點結束。只對使用者自定義部分新增了幾個新資源後就結束了全部的開發工作,開始寫 Playground。

Playground

我給大家一個建議:開兩個工程,一個是以 app 的模式來進行開發的工程,一個是 Playground 工程。寫完 app 一個功能後就立馬在 Playground 中進行復現,直到復現成功且功能正常後,再繼續寫 app。這樣你會爽得飛起。

當然,Apple 給了一個 PlaygroundBook 的模版工程,可以根據這個工程來進行修改。

Playground 遵循大部分的 Markdown 語法,如下圖所示:

Xcode markdown

需要注意的是:

  1. 使用 //: [Previous: The First Part](@previous) 返回上一個 Page,使用 //: [Let's get the first part!](@next) 進入下一個 page,目前來看只能做順序跳轉,如果是純英文命名的 page file,排序是按照字母序來進行的(差點被坑)。推薦在命名前加上 123 來進行標識。
  2. 在 Page 中進行任何一行程式碼修改都會觸發 liveView 重新構建。如果你不想維護一個全域性物件,可以嘗試使用我的這種做法通過全域性與使用者進行互動:
import UIKit
import PlaygroundSupport

public var brocadeType: PJHomeViewController.BrocadeType = .normal
public var brocadeBackgroundColor: UIColor = UIColor.bgColor()

public func start(_ gameType: PJHomeViewController.GameType) {
    let vc = PJHomeViewController()
    vc.brocadeType = brocadeType
    vc.gameType = gameType
    vc.brocadeBackgroundColor = brocadeBackgroundColor
    PlaygroundPage.current.liveView = vc
}
複製程式碼
  1. 一個 Page 都會有一個獨立的 liveView,多個 page 之間互不干擾,所以儘量讓多個 page 之間不要產生關聯。
  2. 每個 page 的 liveView 的 frame 很怪異,不推薦用 UIScreen.main.bounds 的方式獲取螢幕寬高,因為這會獲取到當前 Playground 所執行平臺的螢幕寬高,在 Mac 上這螢幕的寬高就很酸爽了,記住這只是個 demo,螢幕適配的時機不適合現在。

Submission

一切完成後,就要開始寫文案了,今年的 WWDC Scholarship 可以寫以下文案:

  • 介紹你自己是怎麼學習計算機的;
  • 介紹這個 Playground 中運用到哪些技術以及功能點;
  • 如果你有自己的獨立開發的 App,可以介紹一下;
  • 如果你還有什麼想讓 Apple 知道的,可以說一下;
  • 超過十八歲可以把自己的簡歷附上去。

我非常的後悔自己在大學四年中沒有開發出任何一款完全屬於自己的 App,我昨天有點微微難受。希望接下來的畢設能夠給自己一些安慰吧~

簡歷我沒有附,雖然 Apple 明確表示簡歷不會影響最終的評分,但我還是不想。

結束了

當我按下 Submit 時,整個人完全放鬆下來了......

世間還有這麼多美好的事物,我為什麼要和自己這麼過不去?整整一週的時間整個人的心情都是緊張的,前兩三天的時候一直在擔心東西做不完,做的不好,還有多少功能沒有實現。但到按下 Submit 後,管它還有什麼沒做完,也不管最後到底是能不能被選上,只想遠離這個位置。去呼吸新鮮空氣,去看這藍天白雲,去看這紛繁多彩。

原本打算矇頭蓋被睡一覺,但不知為何心情又開始劇烈激動起來,翻來覆去睡不著,買了張《波西米亞狂想曲》的電影票,在電影院裡一個人吃著雞米花,享受著這視聽盛宴,真好!

Playground 工程:github.com/windstormey… App 工程(你可以聽到獨特的黎族歌曲):github.com/windstormey…

相關文章