創建一個跟分辨率無關的 iOS 8 應用
自從iOS退出依賴,png就被用來制作圖標。這已經是如此簡單了,那為什么還會有人會要考慮其他的解決方案呢? 一般的制作圖標的工作流程是這樣的:“我想要新添加一個按鈕,這個action需要一個圖標,設計師把圖標發email給我,我們就完工了.” 如此設計師把這項工作安排到了他的工作流之中,然后按部就班完成了這項工作,之后你就會受到一封Email,里面有2個png文件(retina 和 非 retina 屏幕)
等等... 這個圖標需在改改顏色,以滿足我們想要按鈕呈現高亮狀態的需求. 因此你重復了上述流程,并獲得了另外2個png。實際上,我們還會繼續需要讓這個按鈕在某些特定的情況下呈現禁用狀態,因此我們又會需要禁用狀態的圖標. 好吧,iOS現在又支持@3x分辨率的了, 因此我會需要再輸出這些圖標及其各種狀態的文件… 而設計師現在很忙,短時間內不能為我完成這項工作了?! 如今不能獲得這些資源文件的我如何讓我的應用適配新的屏幕尺寸呢?
任何曾今為那些以開發移動應用為目標的團隊工作過的人,好像大都遇到過上述其中一個或者全部的狀況. 它可能會是非常令人沮喪的,也會讓你的團隊白白浪費許多時間. 在Craftsy這里,我們向后退一步,看看我們嘗試真正要去完成的是什么,還要看看我們是否錯失了另外一種可能的更好的方法. 而當我說后退一步的時候,我的意思是做一次大的退步.
我們嘗試要去完成的是什么?
目標:在屏幕上繪制一個圖標,使其像素完美的適配那個屏幕的分辨率.
在最基本的層面上,這個目標就是我們現 在正在做的. 然后問題就會是,我們是否使用了最有效率和更加務實的方式完成了這個目標? 讓我們來看看我們的選擇。我們可以繼續去構建并維護一個相當簡單的圖像庫,并讓從開發和設計兩頭都承擔保持圖像庫更新的工作量. 或者我們還可以采用一種動態的方式,只去踏踏實實的處理絕對必要的變化和工作,并將時間專注的花在其它問題的解決上.
進入PaintCode, 它是一個 OSX 應用程序,其名字正如你所預想的其功能. 它能解析從PhotoShop創建或導入的矢量圖片資源,然后將其”繪制“成一個編碼庫. 我不會花太多時間向你推銷 PaintCode, 我會建議你上網試試他們的在線解決方案. 不過未來在完成移除我們對于圖像的依賴這項任務方面,會證明使用它是必要的。看待PaintCode及其價格的一個好的方式就是,就好像它是為你的團隊雇 傭的一個開發者. 你要問你自己的是 “你會需要雇傭一個開發者,它可以編寫完美的可控代碼,卻只需要開發應用成本負擔一次性的花費?” 好了,那就是我所想的.
PaintCode, 正在改變開著人員同設計師協同工作的方式.
設計師們其實擁有的大量的誘人數據,這些數據都隱藏域他們的photoshop文件之中。這些數據被稱作矢量圖形. 為什么矢量圖形很重要呢 ? 簡單來講, iOS的世界里現在正面臨的,與分辨率和不同狀態效果有關問題,在設計界早就都解決了. 當設計團隊創建一個圖標的時候,他們很可能會采用一種基于矢量圖形的,使用了線條和貝賽爾曲線的方法. 這就能很好的解決創建“分辨率獨立 ”的問題. 那樣如果我們需要適配屏幕尺寸為1000dpi的圖標,或者只是20dpi的縮略圖,它都可輸出來,而且看上去都是像素完美的!
那么我們就要問了,如果開發者可以簡單的勝任這種現有的技術,會怎么樣呢? 那就會使得我們不用再去過多考慮像素問題了. 雖然這常常成為一種可行的方式, 但和只是持續的繪制和輸出圖形比起來,使用編程的方式用代碼繪制矢量圖形需要花費更多的時間,直到 PaintCode 出現才讓收益大過支出. 并且,大多數的程序員們都沒有進行細節設計的好眼力,所以最終的成果大有可能不會符合預期. 因此,設計團隊的存在其實可以促進 PaintCode Objective-C 文件的輸出.
而通過管理起開發者和設計師間的所有這些代碼,PaintCode一舉消除了所有這些問題 ! 設計師只要將他的矢量圖形從Photoshop導入PaintCode中,做一些小維護以方便同開發者的對接, 瞧, 開發人員現在可以繪制符合任何分辨率需求的圖形資源了.
這一功能開始改變了設計師和開發者之間的溝通方式. 標準一旦建立起來,交談的內容立刻從 “你能把資源發Email給我嗎 ?” 變成了 “讓我知道你什么時候把那個圖標遷入了git ”. 有時候甚至都不要交流;設計師調整完圖標之后,應用程序就會幫他把新的版本繪制出來.
那么是不是我只要買了PaintCode,一切就都會變得更簡單了呢?
可嘆的是,并不是這樣的. 事情并沒有那樣簡單,PaintCode僅僅只是一個工具,而你如何去利用好這個工具將最終決定你是否能成功. 而長遠看來,將PaintCode和Xcode恰當的組合在一起使用,我們就能夠省下大量和時間和美金. 下面我將會描述一下我們如何使用PaintCode實現對圖標資源和圖標不同狀態的管理.
第一步 - 指定一個設計師能配合的方案
開發人員必須是第一個參與進去的,并為團隊設定好PaintCode如何為他們工作. PaintCode 可以加入變量,開發人員可以在最后把這些變量加進去. 我們首先要處理的就是圖標的尺寸.
如果你使用的是正方形,而正方不需要兩 個維度數值(兩個維度的值相等),因此圖標很容易擴展 - 它們只需要一個維度. 因此我們想PaintCode新增了一個公共的(可以用編碼復制) 數值,它被叫做 “size”. 從設計的角度講,我們不能直接使用這個數值, 因此我們想PaintCode添加了一個表達式,來基于那個尺寸(size)計算出要繪制的區域:
PaintCode:
makeSize(size, size)
我們只需要傳入一個尺寸就行了,因為幀的原點總是會在 0,0. 現在,我們遵守的標準如下:
每一個圖標畫圖都必須包含一個幀作為根
這個作為根幀的原點必須是在 0,0
根幀的尺寸必須被綁定到來自用代碼所傳入的參數計算出來的尺寸值
設計將需要符合幀的標準,而現在讓我們假定所有的東西都是基于那個根幀被繪制出來的, 因此是可以無限擴展的.
現在,我們需要通過編寫代碼來改變圖標的顏色。不幸的是,PaintCode當前還不支持直接傳入顏色。不過,就像我之前說的,PaintCode 是一個工具,一切取決于你怎么使用。要處理顏色,我們創建紅,綠,藍和Alpha4個值。在PaintCode中,我們就可以使用如下的表達式:
makeColor(red, green, blue, alpha)
這樣就可以在PaintCode中使用傳入的顏色了。同時,我們的協議改成:
繪制的圖標必須和代碼庫生成的顏色變量綁定。
好了,我們已經可以通過代碼獲取圖標的尺寸和顏色。這種方法對于應用中一般的圖標繪制很有用,不過對于按鈕來說又該如何處理呢?
在iOS中,我們所熟知的按鈕有4種狀態,分別是Normal(常規狀態),Highligted(高亮狀態),Selected(被選擇狀態)和 Disable(禁用狀態)。從PaintCode的角度看,我們希望在設計和用戶體驗上能夠展現出這些不同的狀態。為了解決這個問題,我們添加了一個新 的public參數,叫做drawType。有了這個參數,圖標在繪制的時候就有了多種類型的選擇。我們可以創建4個表達式來決定繪制哪一種狀態:
Normal
drawType < 1 || drawType > 3
Highlighted
drawType == 1
Selected
drawType == 2
Disabled
drawType == 3
每個表達式都會產生一個布爾值。我們把這個布爾值和圖標路徑的可見度關聯起來。也就是說,當drawType是0的時候,只有Normal的圖標可以繪制出來。drawType等于1的時候會隱藏Normal的圖標,同時顯示Highlighted的圖標,以此類推。
現在我們標準里就有了一個叫做drawType的可選參數:
如果圖標需要基于其狀態展現不同的外觀,那就把每個圖標的可見性綁定到所需的狀態上. 而普通的Normal則是默認的狀態.
現在從代碼的角度,我們得到了下面這些東西:
- (void) draw(IconName)WithSize: (CGFloat)size red:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha drawState:(CGFloat)drawState;
這樣下去開發者就擁有了對要繪制的圖標的尺寸,整體顏色以及狀態的控制能力. 沒有必要傳入設備的尺寸,因為這完全是自動處理的. 現在開發者可以把外觀下發到設計團隊,由他們來做出符合標準的設計.
第二部 - 在標準范圍內進行設計工作
設計團隊將資源文件從Photoshop導入PaintCode,并讓資源文件的規格符合上述的設計標準.
添加一塊畫布來管理圖標
添加一個根幀到附上了由計算得出了尺寸,原點則在0,0位置的畫布上
圖標需要作為一個組繪制在根幀的范圍內, 如有必要,附件會適當的以幀對其.
(例如,這個圖片左右撐開(支撐),上下則彈性(彈簧)對齊)顏色被附著到了圖標上
如有必要,這一過程可以在不同的狀態下重復,而這些組的可見性被附著到了狀態的值上 (普通Normal, 高亮Highlighted, 等等.)
現在,沒有任何實際的代碼編寫,PaintCode幫助生成類似于下面的東西給開發人員編碼解決:
+ (void)drawRecoStateIconWithSize: (CGFloat)size red: (CGFloat)red green: (CGFloat)green blue: (CGFloat)blue alpha: (CGFloat)alpha drawType: (CGFloat)drawType; { //// Variable Declarations CGSize sizeCalc = CGSizeMake(size, size); UIColor* fillColor = [UIColor colorWithRed: red green: green blue: blue alpha:alpha]; BOOL normal = drawType < 1 || drawType > 3; BOOL selected = drawType == 2; //// Frames CGRect frame = CGRectMake(0, 0, sizeCalc.width, sizeCalc.height); //// Subframes CGRect recoOrange = CGRectMake(CGRectGetMinX(frame) - 0.01, CGRectGetMinY(frame) + floor(CGRectGetHeight(frame) * 0.11995 - 0.48) + 0.98, CGRectGetWidth(frame) + 0.02, floor(CGRectGetHeight(frame) * 0.88022 + 0.5) - floor(CGRectGetHeight(frame) * 0.11995 - 0.48) - 0.97); //// Reco Orange { if (normal) { //// defaultType Drawing UIBezierPath* defaultTypePath = UIBezierPath.bezierPath; [defaultTypePath moveToPoint: CGPointMake(CGRectGetMinX(recoOrange) + 0.00000 * CGRectGetWidth(recoOrange), CGRectGetMinY(recoOrange) + 0.99740 * CGRectGetHeight(recoOrange))]; [defaultTypePath addLineToPoint: CGPointMake(CGRectGetMinX(recoOrange) + 0.00000 * CGRectGetWidth(recoOrange), CGRectGetMinY(recoOrange) + 0.16623 * CGRectGetHeight(recoOrange))]; ...etc... [defaultTypePath closePath]; [fillColor setFill]; [defaultTypePath fill]; } if (selected) { //// type2 Drawing UIBezierPath* type2Path = UIBezierPath.bezierPath; [type2Path moveToPoint: CGPointMake(CGRectGetMinX(recoOrange) + 0.00000 * CGRectGetWidth(recoOrange), CGRectGetMinY(recoOrange) + 1.00000 * CGRectGetHeight(recoOrange))]; [type2Path addLineToPoint: CGPointMake(CGRectGetMinX(recoOrange) + 0.00000 * CGRectGetWidth(recoOrange), CGRectGetMinY(recoOrange) + 0.16623 * CGRectGetHeight(recoOrange))]; ...etc... [type2Path closePath]; [fillColor setFill]; [type2Path fill]; } } }
第三步 - 適應代碼由PaintCode產生
一旦你有你產生PaintCode實現,這真的取決于你希望如何彌合到你的代碼。一種選擇是直接執行帶有參數的方法。我們選擇了選項2,包裝方法,簡化了一些從PaintCode要求.
例如,在色彩傳遞的時候,它更方便使用的UIColor不是一個接一個打出來的RGBA值。因此,對于RGBA轉換器來說一個簡單的UIColor是一款 不錯的選擇。如果我們需要,我們還具有把這些方法渲染成圖像的能力。使用包裝,讓我們在彌補某些基本空白給上有了更多的選擇,以延長由PaintCode 生成的代碼,我會強烈建議采取這種方法.
PaintCode 所提供的其它優勢
除了圖片路徑之外,PaintCode 也是一個可以引用其它資源的很棒的資源庫. 我們還用它來管理和組織下面這些東西:
顏色 - PaintCode 提供了一個很好的調色板界面. 我們使用的是 [PaintCodeLibrary colorCustomOrange], 而不是[UIColor colorWithRed/Blue/Green/Alpha] .
漸變 - 用PaintCode溝通像漸變和覆遮罩這樣的設計元素容易了很多. 你可以用編程的方式編輯透明度值,使漸變更加的精確,且容易修改.
陰影 - 陰影可以被存儲并且也能夠被開放訪問, 這就允許設計能控制整個通用的陰影.
Swift:因為PaintCode只是在生成你使用的代碼,把它轉換成一種新的語言(像Swift)就只要點擊一下按鈕這么簡單.
這一切值得嗎?
當然值得。從解決“依賴”中釋放自己是一個巨大的進步,在一個生態系統之上,有效節省了團隊開發iOS應用的時間,還解決了現在頭痛的事情。
PaintCode在之前一段的時間里,是不太實際的,但是現在可以了。這種方式讓你的團隊把注意力更多地集中在問題上,他們應該解決問題而不是重構,去調節新的屏幕和分辨率,去把焦點集中在建立一個更穩定,更效率的應用上吧。