MGTileMenu:一個開源iOS控件的誕生
英文原文:MGTileMenu
導讀:Matt Gemmell 是歐美知名 iOS/Mac 開發人員,現居蘇格蘭愛丁堡。Matt 圍繞他近期發布的一個開源 iOS 控件 MGTileMenu 寫了兩篇博文。在驚嘆國外頂尖開發人員對細節精益求精的專業態度之余,我們是否也應該反思國內從業人員低水平重復的現狀與深層次原因?
很高興宣布我的又一個開源 iOS 控件 MGTileMenu 發布了。這個控件基于 iOS 5 開發,使用了 ARC(譯者注:Automatic Reference Counting),能提供具有關聯性、基于磚形按鈕的彈出式菜單,對 Retina 和非 Retian 顯示屏都有很好的支持,同時還支持 VoiceOver。MGTileMenu 最初是為 iPad 應用開發設計的,但把它用在 iPhone 和 iPod Touch 應用里也是可以的。
你可以基于署名授權許可證(Attribution License)免費使用 MGTileMenu,也可以付點費用而免去署名授權協議的限制。MGTileMenu 沒有使用其它第三方資源。在 Retina 分辨率下,看起來是這個樣子:
彈出菜單每一頁會顯示最多 5 個圖標,而第六個省略號圖標用來引出下一頁的按鈕,你可以擁有任意數量的頁面。這里有一個 demo 的視頻:
注意翻頁按鈕(…)的位置可以通過設置 MGTileMenu 的左右手使用習慣屬性而改變,同時這個控件還考慮的對用戶手指遮擋區域的留白處理。
MGTileMenu 提供了委托協議(delegate protocol)以方便開發者對按鈕圖標、背景(支持圖標、漸變色、純色)的深度定制。
這是使用了一套表情圖標定制的菜單
MGTileMenu 同時還拋出了各種通知消息(notifications)以方便開發者調用。當然,為了獲得最佳的用戶體驗,我在開發這個控件的時候大量使用了 Core Animation 技術。
我本著方便開發者調用的初衷設計了 MGTileMenu 控件:它的缺省界面和操控表現已經可以滿足大部分場合的調用。此外控件的一些智能化處理也將降低調用者的開發量,例如:它會根據開發者告之的屏幕顯示位置,結合屏幕邊際距離、設備旋轉能因素,綜合計算后安排菜單最終顯示位置,以避免控件顯示在屏幕外等尷尬的結果。隨控件附帶的 view controller 也是按照方便使用的原則設計了屬性、方法及委托協議。總之,你會發現集成 MGTileMenu 到你的項目中是很容易的一件事兒。
在代碼資源里我還放了一個 demo 程序,用來展示如果配置生成一個例子菜單。運行這個 demo 后,在屏幕任意位置雙擊可以喚出 MGTileMenu 菜單。當然,在你自己的應用開發中,你可以通過單次觸碰等更自然的方法喚出 MGTileMenu 菜單。
許可證及捐贈
MGTileMenu 是基于署名授權協議(Attribution License)發布的。同我的其它代碼一樣,我開發并免費發布這個控件是為了給 iOS 及 Mac 開發社區提供一點貢獻,我本人很自豪成為這個社區里的一員。
為了支持這個控件的不斷更新(同時為了支持我的博客),請考慮捐贈或者購買非署名授權許可證。
你可以向我的 PayPal 賬號 (matt.gemmell
at Gmail)捐助,我會非常感謝,并通過未來的博客文章以及代碼做出回報。
如果你不想在你的應用中提供本控件的授權署名信息,請通過我的在線許可證商店購買非署名授權協議。感謝支持!
下載代碼
你可以在 github 上下載 MGTileMenu 的代碼
支持,bug 及功能請求
我不承諾對這個控件提供任何支持。哥們,遇到問題你只有靠自己了。當然,你可以通過 github 上的 issue tracker 提交新功能請求。如果想提交 bug 報告,也請通過 GitHub 的 issue tracker,請務必同時附上你對 bug 的分析及修復方案(要源代碼哦)。我想,作為使用者,你一定是一個有能力自行分析并修復問題的合格開發者,對不?同時歡迎在 github 上提交 pull 請求或者補丁申請。
再啰嗦一次:請不要提交不含分析及推薦修復代碼的 bug 報告!
聊聊 MGTileMenu 的設計
設計 MGTileMenu 的初衷是為了發掘一種新的 UI/UX 方式,當用戶觸摸 iPad 屏幕后,提供具有關聯性的可選項或者工具按鈕。我在開發一個應用的時候設計了這個控件,后來我覺得有必要把這個控件單獨發布出來。
在此之前,我也做過一些嘗試,比如為每一個手指分配工具選項, 但是這次我想做的更加自然,用戶體驗更加有質感一些。我把最初的想法畫在本子上。
最初的設計草圖
不難看出,最終的成品和上面的草圖差別不大。就這樣,憑著一個草圖和聊聊幾筆注釋,我打開 Photoshop 開始了第一版設計:
第一版設計
請注意,我把每一個菜單按鈕方塊的尺寸設計得和 iPad 屏幕上的應用圖標一樣大,這樣可以給用戶提供一個使用習慣上的延續性。但是,最初我在 PS 里面繪制按鈕的時候,只是憑感覺拉了一個尺寸,結果正好是應用圖標的大小,這完全是一個美妙的巧合!
看著第一版設計,我總感覺少了點什么:這些散布的按鈕應該用什么東西把他們聯合起來?很自然的,我想到了在按鈕后面疊加一層面板:
圓形的背板及連接線
可是,圓形的背板看起來太單薄了,它承托不出前面按鈕矩陣的氣場;那些輻射狀的連接線看起來也很雜亂,使得功能按鈕和關閉按鈕直接的關系變很令人費解。這些線必須干掉。
包裹式的背板
接下來我用了一個可以完全包住所有按鈕的圓角背板,感覺還不賴,但是少了幾分活力,多了幾分沉重。我理想中的背板應該更具動感,以迎合之后我通過 Core Animation 編寫的動畫效果的氣質。
最終設計
所以我把圓角背板的尺寸裁剪了些許,這樣既整合了四周的按鈕,又為中心的關閉按鈕提供的背景支撐。事實上在我看來,懸于背板邊緣的按鈕布局多了幾分活潑和有趣的意味。
在最終設計稿基礎上,我通過代碼實現了一切。下面是一個運行的實例,呈現的是缺省顯示風格:
缺省狀態
下面是使用一些定制化背景的效果。
MGTileMenu 第二頁
我個人對最終的效果很滿意。我想這是一個靈活、漂亮、吸引人的控件。接下來讓我們看看用戶交互設計及動畫效果。
用戶交互設計
設計伊始,我十分渴望能在用戶手勢操作上做出一些亮點。當我把自己的手放在 iPad 屏幕上,我發現我們會自然的把手向內側彎曲,這使得我們的手指在屏幕上的滑動路徑是斜向的而不是直上直下的。
因此,在這個控件的左下角(針對左撇子用戶)或者右下角(針對右撇子用戶),我安排了一些的留白,這里不能安排按鈕,以避免被用戶的手遮擋,事實上為了得到最佳效果,我留出了兩個按鈕的留白位置。
左右手使用習慣可以配置
這樣的留白設計同時也減少了在一頁中可顯示的最大按鈕數量,這其實是件好事,我希望以此激發開發者在他們的應用中減少可選操作項,以體現簡潔美。
注意,在為左撇子用戶布局按鈕順序的時候,我不是簡簡單單把右撇子的按鈕排序鏡像反轉過去的哦。因為我不認為左撇子的閱讀、理解順序也是反著的。當然通過委托協議你依然可以堅持翻轉按鈕排序如果你認為有必要的話。
好了,談了這么多靜態的布局和外觀,現在我們看看動畫效果。基于前面談到的各種因素,我立刻想到我們需要以下幾處動畫效果:
1. 菜單的顯示和消失。
2. 菜單換頁時。菜單的顯示消失動畫很容易想:出現的時候是一個放大加淡入的效果,消失的時候是一個縮小加淡出的效果。難度在從一頁菜單切換到下一頁菜單的處理,因為同時要替換頁面上所有的按鈕。
基于按鈕的布局,因為它們看起來都是浮動在背板邊緣的,我想到的動畫效果是:當用戶選擇換頁的時候,四周的按鈕被吸收到中心位置,替換成下一頁按鈕的圖標,在釋放回到原位。
我的最初實現看起來很傻很天真,我用慢動作回放一下動畫效果給你們看一下:
同步的吸放動畫效果
后來我在每個按鈕的動畫效果前都安排的一些滯后,這樣出來的效果就好多了:
布進式的吸放動畫效果
而按鈕的替換我用了基本的淡入淡出效果,這樣的替換看起來不那么唐突。
上面演示的動畫是最終版本的效果。實際上,一開始這些按鈕的動畫效果是按照從左到右、自上而下的順序逐個激發的:
按鈕動畫激發順序
可惜這種激發順序產生的綜合效果看起來比較雜亂,尤其是從上一行最右邊按鈕到下一行最左邊按鈕的過渡很不協調。后來我嘗試了從翻頁按鈕(…)順時針激發按鈕動畫,因為…按鈕是用戶觸發整個切換場景的開端。所以最終的效果就是自翻頁按鈕(…)上面的那個按鈕開始,順時針繞一圈,逐個按鈕吸收、替換、釋放。如果配置成左撇子用戶,則是反順序的。
最終動畫激發順序
動畫處理上畫了我一點時間,但是看著最終的效果還是覺得很值得。
當然,內部代碼以及委托協議里面對按鈕的排序還是基于從左到右、自上而下的自然邏輯,這里我就不折騰調用控件的程序員了。
后記
我個人很享受制作 MGTileMenu 的過程,同樣我希望這個東西對你有所幫助,包括這篇文章 — 無論是設計還是編碼,我注入的全是愛與激情。 不厭其煩的再次插播廣告,請考慮通過捐贈或者購買非署名許可證的方式支持我的代碼和博客。
另外,我透過 MGTileMenu 開發過程的另外一個視角寫了另外一篇博客: API Design, and why I designed it the way I did(譯者注:此篇稍后翻譯) 。對于開發者來說,API 就是他們的用戶界面。在那篇博文里,我以 MGTileMenu 為例,闡述了自己對 iOS 及 OS X 組件開發的一些觀點。
預知更多信息,請粉我的推 (@mattgemmell). Enjoy MGTileMenu!
英文原文:Matt Gemmell 編譯:伯樂在線 – 陳遠