從零開始實現一個iOS APP

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"毫無疑問,新冠疫情在很多方面都深刻的影響了我們的生活。不論是出門必須戴口罩,在戶外保持社交距離,還是外出回來要使用6步洗手法洗手,又或者由於遠程辦公而形成的物理隔離帶來的心理上的負面影響等等,這些都深遠的影響了我們的日常。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我今天要分享的一個故事正是與此相關:我是如何在維州宣佈進入災難狀態的一個多月後,不得不全天幾乎24小時呆在家裏的情況下,從零開始進行一個iOS App開發並最終上線的故事。而這一切自然要從原始的需求分析開始說起。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"原始需求分析"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個需求其實由來已久。在平時購物的時候,爲了避免花費太多時間在超市,很多人都會隨身攜帶一個購物清單,像下面這張圖上列出來的那樣:"}]},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/c2\/c24324302ac2fd29c8246ffe19cce579.png","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大家平時買東西也可能有類似這樣的紙質的購物清單吧。這種購物清單便於攜帶,隨用隨扔,非常方便。不過有幾個小的缺點:一是買完這次之後,下次再去買的時候很可能卡片不知道丟到哪裏去了;二是如果不配合筆一起使用的話,很可能要檢查多次清單才能確定某樣商品是否已經買過。此外,紙片上的信息畢竟有限,有時候你想要描述類似這樣的需求:在永輝超市買甘竹牌豆豉鯪魚,不要錯買成鷹金錢,就會發現紙和筆可能不夠用了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我希望有一個手機App可以幫我管理這些清單。因爲手機基本上是我出門必然會帶的設備,手機本身可以作爲紙筆Todo的替代品,而且有圖片作爲參考可以快速定位並找到我需要的商品。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另外一個典型的購物場景是:我每次購物往往會去多個超市,有的超市新鮮果蔬比較好,但是價格偏貴;而另一家則百貨齊全,有時候水果不太新鮮;另外有一些商品比如豆腐乳,胡椒粉之類只能去亞洲超市才能買到。這時候我希望這個App可以提供分組,我可以在某家超市買完所有想要的商品之後,再換一家買剩餘的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"災難狀態的封城在某些方面促進了這個想法的實現:首先被關在家裏不能外出的我平白多了很多時間,用這段時間來學習一項新的技能顯然是可行的;其次每次購物必須速戰速決,儘量避免在人多的地方待太長時間而增加感染新冠的可能性;最後每個家庭每天只能有一個人出去購物,需要將清單儘可能細化然後實施(軟件可以在某種程度上簡化這個過程)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當然,蘋果自帶的Notes或者Reminder都有類似的Todo管理,但是並不專門面向買菜。我設想的這個應用專門用來管理購物清單。在實施方面,我不打算從Hello world這樣一步步從Swift的基本與反學起,而是先確定要做的App的主要功能,然後帶着問題一邊學習一邊實踐,最終把它演進成一個公開發布的工具。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"實施"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"現在我終於到了我有一個價值千萬的idea,就差一個程序員的階段了。而巧的是,我就是個程序員。唯一的問題是,我的Swift和移動App開發經驗約等於零(雖然多年前在項目間隔中參加過張帥的Android和黃磊的iOS workshop,但是時間隔得太久已經基本上還回去了)。此外,一個產品還有很多雜項,比如界面設計,圖標,legal相關,App發佈流程等等,這些對我來說完全都是未知數。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"在實踐中學習"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通常來說,我比較喜歡通過循序漸進的方式來學習,從簡單的例子開始,逐漸學習各個主題(如何設計界面,如何連接數據庫,如何使用網絡等等),這種方式的好處是基礎會比較紮實,而且很多細節都可以得到充分練習。不過缺點是戰線太長,往往需要耗時數月而沒有實際產出。也因此如果過程中被一些其他事情耽擱,就很難再連續起來。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另一種方法是結果導向,然後用結果反過來驅動需要學習的內容。比如你想要開發一個本地的圖片編輯App,那麼肯定需要學習訪問攝像頭,訪問本地文件系統等,而無需考慮網絡和數據庫。這種方式的優點是可以提供實時反饋,並且時刻有明確目標。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同樣的,這種方法也有一些缺點:"}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"會花費很多時間在Google和Stackoverflow上"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"很多資料都嚴重過時了,很多object-c的資料,以及很多swift老版本的資料"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大部分人分享的內容,都比Hello world級別稍微複雜一點點,但是基本上不可能直接應用到你的應用中"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因此在這個項目裏,我嘗試將兩者結合起來。比如在第一個milestone裏,我通過結果導向的方式,僅僅浮光掠影的學習必要的知識點,比如列表視圖,iOS裏的MVC,navigation之類。主要課程就是參考youtube上衆多的入門教程:通過一個簡單的例子,手把手的寫一個Todo之類。這個過程可以熟悉編輯器,快捷鍵的使用,Swift的簡單用法,界面設計器的使用等等。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個階段之後就可以開始“野蠻”編碼了,開始的時候並沒有特別的章法,就是照着例子寫就好了。如果進行的順利的話,很快就會有一個簡單的原型出來,但是過程中會遇到海量的問題。小到如何添加圖標,配置顏色,大到兩個view之間如何通信,如何同步數據。不過作爲一個有工程經驗的程序員,大部分問題都可以通過Google+Stackoverflow解決。實在自己找不到答案的,還有很多熱心的同事可以請教。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果說我有一丁點成績的話,無非是把別人用來思考變量名的時間花在了嘗試Google關鍵字上了而已。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"right","origin":null},"content":[{"type":"text","text":"                                                                                            -- 魯迅"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"過了這一個階段之後 - 有了一個可以工作的原型 - 就可以好好的打磨它了。於是我開始系統的學習udemy上的課程,比如如何使用使用protocol模式,如何使用MVVM,使用segue的正確方式等等。這時候可以適當的做一些重構和修一些簡單的bug。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下圖是從第一個原型到當前最新版本的用戶界面的演進示意。從一開始的原生的TableView到定製的單元格,再到對數據按照超市信息分組並展現,每一次都比之前略複雜一些,而且每次的修改基本上都是基於客戶的反饋來改進的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/89\/89a6886adb93e7313141c99d96e6ff93.png","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此處的一個建議是:如果時間充裕,還是建議從基礎學起,循序漸進,這樣的方式可以確保基礎比較牢固。而如果時間有限,又害怕被別的事情分心,那就可以邊做邊看,用到了再學,等建立起正向反饋之後再回過頭來查漏補缺。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"項目管理"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在實施過程中我發現:一個可視化的,有跡可循的故事板非常有用。當然比這個故事板更加重要的是你需要制定一些規則並確保執行。比如,所有的卡片都應該從Backlog開始(即所有的需求都要經過仔細分析),開始前需要和stakeholder確認優先級,在開發過程中需要及時收集反饋,有明確的DoD(definition of done)等等。簡而言之,儘量將其當成一個正式的項目來運作。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另外我還發現,之前在客戶那裏見到的諸多反敏捷的實踐,我自己也往往會再犯一次。比如不及時更新卡的狀態,臨時插入"},{"type":"text","marks":[{"type":"strong"}],"text":"Doing"},{"type":"text","text":"任務或者不限制在製品數量,不及時從stakeholder那裏收集反饋,驗收條件不明確等等。不過後來就好很多了,我儘量讓其變得正式一些,而這個過程也確實可以幫助我更加聚焦在高優先級,更具價值的(而不是更fancy)的功能上。"}]},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/4c\/4c268169b2ef6034f2bf480526a9709c.png","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個大約是最簡單,投入成本最小,又可以產生很好效果的實踐之一了。特別是如果被一些臨時任務中斷之後(比如過了一個週末,或者項目上很忙晚上要學一些其他資料),一個故事板可以快速的幫你建立上下文並快速進入狀態。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"上線"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"經過了大約兩週之後,我註冊了Apple developer賬號,並提交了第一個原型。第一個原型包含了三個列表(一個商品catalogue,一個待買列表,一個推遲列表)結果被無情reject了,原因是……功能太簡單。仔細分析之後,我發現功能雖然不算單調,但是界面太過於原始,很多界面元素也需要調整。於是有花費了一週的時間來調整設計,比如字體的選擇,字號,色彩對比等,以及實現了共享列表功能(可以將你的購物清單通過Airdrop或者微信等發給別人)。第二次提交之後,3個小時後就審覈通過了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"V1的開發大約耗時3周時間,每天晚上學習並編碼2-3個小時左右,週末會稍微多一些。隨後V2也差不多2-3周,除了功能開發之外,還有些周邊的定義,比如screenshots海報的設計和實現(詳見下一小節)。不過我最近發現To Buy在iOS 14上有個bug(原因是iOS自身對NSFetchedResultsControllerDelegate的更新上有個同步的bug,如果你正好知道解決方法的話,請不吝賜教,非常感謝),所以如果你使用的以後發現什麼異常的話,請反饋給我。此外,所有代碼都在Github上,如果有感興趣的同學也可以一起來完善(野生iOS程序員寫的代碼,請輕拍)。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"To Buy應用"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個應用叫To Buy: grocery shopping list,目前已經在App Store上線了。這是我的第一個從界面設計,到編碼實現,到架構及運維(雖然目前主要用戶只是我和我老婆),以及圖標和海報的設計,文案的編寫等完全端到端的產品。雖然沒什麼用戶,不過作爲第一個App,我自己還是挺滿意的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/f5\/f54b1d22d664ac26dc2241d999b9b733.png","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"特性列表"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"再經過了幾個版本(當前版本V2.5.0)的迭代之後,現在的To Buy的主要功能就是:維護一個購物清單。用戶可以:"}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過拍照\/相冊裏的照片,文字等來添加一個要買的商品"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"商品會按照超市分類,這樣我就可以在A超市買所有果蔬,在B超市買日用等"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以把歷史上買過的商品存到字典裏,方便下次購買"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以將購物清單分享給另一個人(比如通過Airdrop)"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以把Apple自帶的Photos\/照片中的照片直接添加到To Buy中"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所有數據會同步到iCloud,即跨設備可以完全同步"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在迭代中,我發現了一個很有意思的現象:不論我當初如何篤定某個特性的設計,它的最終形態都會和開始的時候想法相去甚遠。比如最早的想法是個內置一個大而全的商品字典,然後用戶從這個字典裏選擇商品。但是最終發現這樣的操作效率很低,特別是在手機上操作的時候。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最終To Buy演化成可以通過照片\/攝像頭添加,然後快速編輯等。還有一些功能,比如一鍵分享你的清單給別人,讓TA幫你買東西等,也是在使用者發現的新的功能點。甚至,最開始的三個獨立的頁籤也被簡化成了一個頁面。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"一些心得"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在這個從想法到實踐再到產品上線的過程中,我有了一些對做一個產品(即使是一個很小的,只是爲了解決我自己生活中一個小問題的產品)的新的認識。總結一下,大致可以分爲這樣一些方面,希望可以對你也有用。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"不要想太多"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"和對一切都充滿興趣的新人程序員比起來,有經驗的程序員往往有一個功利心太重問題。他們更傾向於分析學習某項技術的投入產出比,該技術的前景等等。誠然,在學習之前做一些功課來確保自己的時間不會白費當然無可厚非,不過需要注意的是:不要花過多的時間在"},{"type":"text","marks":[{"type":"strong"}],"text":"考量"},{"type":"text","text":"本身上。首先這個對比本身就會佔用一定時間;其次,在做了各種理性客觀的對比之後,有可能會忘記自己的出發點(甚至產生已經掌握了該技術的錯覺);最後,很多問題是表面上看不見的,只有在動手解決問題的時候纔會意識到其中涉及細節之深廣。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"解決方法就是不要想太多,在做了初步調研之後,不妨動手用這些技術來解決一個具體的問題,並通過解決問題來學習並和該技術進行交互,從而得到更好的提升。另外,解決問題帶來的成就該又回驅動你進一步的探索。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"不要追求完美"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"正如人們常說的那樣:Perfect is the enemy of good。這一點在做一個產品的時候尤爲重要,不論是看起來更加健壯的架構風格,還是完善的自動化測試,又或者正確的使用MVVM模式,這些的優先級都應該是略低於產品的"},{"type":"text","marks":[{"type":"strong"}],"text":"主要功能"},{"type":"text","text":"的。如果想要設計一個完美的架構在開始進行編碼的話,我懷疑幾乎不可能有那一天的到來。此外,追求完美可以是一個漸進的過程,我們可以從具體的問題開始簡單實現,"},{"type":"text","marks":[{"type":"strong"}],"text":"逐步的"},{"type":"text","text":"將其變得完美。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們當然需要不斷的迭代並打磨產品中的各個細節,有時候甚至需要逐像素的調整一些視覺元素,但是優先級更高的是將主體搭建起來,然後再考慮局部的優化。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"如果不確定哪種方案,選擇簡單的"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在選擇某個技術方案時,儘量選簡單的方案。如果使用文件系統就滿足需求,那就不要用數據庫;如果本地計算就足夠,不要選擇網絡。在工程中,每一個額外的集成點都會有很多難以預料的失敗機率。而這些失敗機率會累積並放大,形成更大的,幾乎必然的的錯誤\/失敗。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"換句話說,延遲對架構的決策,直到萬不得已。另外,我們總是有機會回過頭來在做調整的,也正因爲如此,在做調整時也始終選擇比較簡單易行的方案,這樣即使要重寫的話也更容易。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"細節中的魔鬼"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在這個過程中,另一個重要的心得是很多高大上的、振奮人心的功能事實上並不會花很多時間,反而是在一些不起眼的功能諸如i18n,或者accessibility,從異常中恢復等等輔助性功能上會花費很多時間。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/7a\/7a6bdf145e053a0e586a00d27fa9b876.png","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如在實現"},{"type":"text","marks":[{"type":"strong"}],"text":"dark mode"},{"type":"text","text":"的時候,我們需要考慮背景色,高亮色,陰影,文字對比度等等,而且需要在各個場景下都即可用又好用,還不能讓用戶感受到設計的存在。這些隱藏的細節會花費大量的時間和精力,甚至需要很多重複的純體力的勞動,但是你仍然需要像接受其他部分的特性那樣接受它們。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而要做好一個產品,你需要靜下心來仔細打磨那些邊緣的,支撐性的功能。這些看似出力不討好的特性,就像是在讓你感覺到飽的之前喫掉的那些饅頭一樣不可或缺。在做計劃時,也需要將這些因素都納入考慮範圍。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"小結"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"道德經有云:合抱之木,生於毫末。任何一個複雜的產品都起始於一個簡單的想法。而在將一個想法逐步轉換到產品的過程中,即使像To Buy這樣一個簡單的業務,也會有很多值得反思和琢磨的細節。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先,除了明確目標之後的堅持不懈之外,還需要有意識的將這個目標breakdown成小的、可度量的任務,並賦予優先級並逐步的實現。在上面的情況中,我自己恰好是"},{"type":"text","marks":[{"type":"strong"}],"text":"stakeholder"},{"type":"text","text":"之一,學習iOS開發是項目的一個重要目標,因此這個目標也會參與到優先級的排列。這一點在很多的客戶項目中則並不總是現實情況。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其次,在實現細節中,大可以在有了初步設想之後就大膽的做實驗,以粗糙但目標明確的方式實現原型,然後再以此原型爲基礎逐步完善。對於拿不準的設計或者技術方案,也不用過度考慮,選擇相對簡單易於實現的選項。大部分複雜性則可能當你深入到足夠細節中之後才逐步浮現,此時的決策反而較"},{"type":"text","marks":[{"type":"strong"}],"text":"事先的詳細規劃"},{"type":"text","text":"更爲可靠和可行。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此外,將整個實現的過程看成一次\/一系列實驗的另外一個好處是,你通過調整不同的參數來觀察組合的結果,並動態的修正目標,最終到達一個比較貼合實際的成果。退一步來說,不用將最終結果看的太過重要,不妨將其作爲一次"},{"type":"text","marks":[{"type":"strong"}],"text":"有趣"},{"type":"text","text":"的學習過程,至不濟也可以爲你下一個想法到產品的過程提供可以依據的經驗。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文轉載自:ThoughtWorks洞見(ID:TW-Insights)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原文鏈接:"},{"type":"link","attrs":{"href":"https:\/\/mp.weixin.qq.com\/s\/g_AgKTafTrPySWN1eVsiuQ","title":"xxx","type":null},"content":[{"type":"text","text":"從零開始實現一個iOS APP"}]}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章