本文由CocoaChina譯者 @唧唧歪歪 翻譯,做者:Hector Matosios
惡夢xcode
想象你正在幹活,你的上級要求你在工程中加入一些圖片。你找到團隊中的設計師(若是大家有一個的話),鼓起勇氣,問他要這些圖片。不過,一般他都會忙得根本沒時間幫你。由於他要作的活兒比你還多。說實話,你到底見過一個設計師有多少活兒要作嗎?!簡直多到使人髮指啊!因此你最不想作的就是成爲那個再給他加活兒的人了。特別是那些活兒對設計師來講簡單得讓人厭煩。更不用說,設計師也要按順序幹活,你拿到那些圖片也是幾天以後了。因此咱們仍是來看看如何用Xcode 的Asset Catalog來處理這些麻煩事吧。app
麻煩事No.1:「能改一下這個圖片的顏色嗎?」佈局
目前爲止iOS已經提供了一些至關複雜的方法來處理工程中的圖片,但很不幸的是,卻沒有用來處理這種狀況的方法。我已經數不清楚有多少次咱們由於什麼便捷性支持或者是客戶不喜歡而改變工程的顏色了。如今在工程中全局改變UIColor已經不是什麼難題了,可是咱們還要改變工程中圖片的顏色。這樣咱們就不得不回去找已經忙得要死的設計師了,就由於咱們太懶而不去學學怎麼用Photoshop。固然咱們也能夠寫代碼來完成,不過看起來至關複雜,並且還容易形成泄漏。在iOS7 以前,寫代碼改變圖片的顏色和下面差很少(還有不少其餘的方法):ui
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
class func image(name: String, withColor color: UIColor) -> UIImage? {
if
var
image = UIImage(named: name) {
// begin a new image context, to draw our colored image onto. Passing in zero for scale tells the system to take from the current device's screen scale.
UIGraphicsBeginImageContext(image.size,
false
, 0)
// get a reference to that context we created
let context = UIGraphicsGetCurrentContext()
// set the context's fill color
color.setFill()
// translate/flip the graphics context (for transforming from CoreGraphics coordinates to default UI coordinates. The Y axis is flipped on regular coordinate systems)
CGContextTranslateCTM(context, 0.0, image.size.height)
CGContextScaleCTM(context, 1.0, -1.0)
// set the blend mode to color burn so we can overlay our color to our image.
CGContextSetBlendMode(context, kCGBlendModeColorBurn)
let rect = CGRect(origin: CGPointZero, size: image.size)
CGContextDrawImage(context, rect, image.CGImage)
// set a mask that matches the rect of the image, then draw the color burned context path.
CGContextClipToMask(context, rect, image.CGImage)
CGContextAddRect(context, rect)
CGContextDrawPath(context, kCGPathFill)
// generate a new UIImage from the graphics context we've been drawing onto
image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return
image
}
return
nil
}
|
在兩個大版本升級以後,我還能看到這樣的代碼。在iOS7中,咱們有了imageWithRenderingMode,這是UIImage的一個方法,參數是有三個選項的枚舉值UIImageRenderingMode。spa
1
2
3
4
5
|
typedef NS_ENUM(NSInteger, UIImageRenderingMode) {
UIImageRenderingModeAutomatic,
// Use the default rendering mode for the context where the image is used
UIImageRenderingModeAlwaysOriginal,
// Always draw the original image, without treating it as a template
UIImageRenderingModeAlwaysTemplate,
// Always draw the image as a template image, ignoring its color information
} NS_ENUM_AVAILABLE_IOS(7_0);
|
UIImageRenderingModeAlwaysOriginal 就和字面的意思同樣,這個模式告訴系統按照圖片文件原來的樣子渲染圖片。翻譯
UIImageRenderingModeAlwaysTemplate 這是最有意思的模式。首先會掃描你的圖片,而後從圖片中全部不透明的像素建立一個模板。這同時也會忽略圖片的全部顏色信息。你可使用UIView子類的tintColor屬性來給圖片填充你選擇的顏色。設計
UIImageRenderingModeAutomatic 這個模式由系統根據圖片的使用環境來決定如何渲染圖片。若是你的圖片是用在好比UITabBar、UINavigationBar、UIToolbar 和UISegmentedControl這些地方,圖片使用AlwaysTemplate渲染模式。圖片用在其餘的地方則會使用AlwaysOriginal渲染模式。3d
在瞭解了上面的內容以後,以前咱們改變圖片顏色的代碼就能夠簡化成下面這樣的了:
1
2
3
4
|
if
var
imageToChange = imageView.image?.imageWithRenderingMode(.AlwaysTemplate) {
imageView.image = imageToChange
imageView.tintColor = .redColor()
//Setting the tint color is what changes the color of the image itself!
}
|
是否是很神奇?用代碼改變圖片的顏色,如今變得簡單多了。
等等,還沒完呢!其實不須要代碼也能夠改變圖片的顏色。
從Xcode 6開始,imageWithRenderingMode已經集成到Asset Catalog裏了。若是你在Asset Catalog裏選擇了一個圖片,在右邊的Attributes Inspector裏,就能夠像下圖那樣把Render As選項改爲Template Image。
就是這麼簡單。甚至你還能夠經過在Storyboard中,在Attributes Pane中改變UIImageView的tintColor屬性,來改變imageView中的圖片的顏色。
麻煩事No.2:「能給我這個的3X分辨率的圖嗎?」
這個的確很煩,由於每一個app的每一個設計師被問這個問題都至少一年了。貌似蘋果每一年都會增長一種新的屏幕分辨率,今年我也持懷疑態度。隨着硬件技術的發展,蘋果老是走在前沿,老是在儘量地提升屏幕的ppi。很不幸,這意味着咱們不能直接在「預覽」中放大已有的圖片,由於這會形成諸如圖片像素化和產生鋸齒等問題。通俗點,就是咱們的圖片變醜了,噁!每次你叫設計師出一張已有圖片的3x分辨率圖,某個地方就又要死一隻獨角獸了。這實際上也解釋了爲何如今看不到這種神奇的生物了。
因此去年我在WWDC上提到的最好的消息,就是Xcode 6 及以上版本支持在Asset Catalog中使用矢量PDF了。你的設計師知道這是什麼意思,可是大體上,PDF是矢量元素的事實標準。矢量文件包含一個元素的不少元數據,用來告訴系統如何渲染這些內容,而這些和屏幕分辨率無關。舉個通俗易懂例子,一個圓形的矢量PDF圖,當它渲染成5像素寬和渲染成5000000像素寬時是同樣清晰的。
在iOS平臺,Xcode是在編譯時,根據你的矢量PDF圖的大小,生成1x、2x和3x圖。若是你的PDF圖是45*45px,那麼Xcode會在編譯時生成下面3個PNG:
45*45px :1x設備用的(iPhone 3G and 3GS)
90*90px :2x或Retina顯示設備用的(iPhone 4, 4S, 5, 5S, and 6)
135*135px :3x設備用的(iPhone 6 Plus 及以上)
這也意味着當有更高的屏幕分辨率時,Xcode能夠根據已有的矢量PDF放大圖片,這樣自動就支持之後的設備了。還有,若是你是OS X開發者,那麼矢量PDF就更好用了,OS X app徹底支持矢量PDF,你能夠用代碼縮放圖片而不會失真。
而你須要作的就是,找你的好基友設計師拿到這些矢量PDF文件,而後在Asset Catalog的Attribtues Pane中,在Scale Factor的下拉框中選擇Single Vector就好了。
你能夠直接把PDF拖到Asset Catalog中,而後進行設置。
麻煩事No.3:「能給我新設備的啓動圖嗎?」
啓動圖對於app來講仍是蠻重要的。這是啓動app後最早看到的,它會給用戶一個app其他部分是如何設計的第一印象。若是我看到一個設計得很糟糕的啓動圖,我會認爲app其餘地方也好不到哪去,固然這只是個人狀況。對於咱們那可憐的設計師來講,每次有新設備出來時,他們都知道要放大啓動圖來支持新設備的分辨率。對於iPhone 6 和 iPhone 6 Plus,若是你沒有爲這兩個設備準備對應的啓動圖,那麼app就會工做在放大模式。啓動圖還在Asset Catalog中,可是我建議把它拆出來,由於啓動圖也升級了。如今,你可使用LaunchScreen xibs。
在工程文件中,你能夠指定app在啓動時加載的xib,這樣你就不須要準備9張啓動圖了。LaunchScreen.xib還支持自動佈局,這樣咱們就能分塊構建啓動屏幕了。按以下這樣設置:
首先建立一個xib文件。你能夠在以下圖所示的地方選擇Launch Screen類型的xib。
而後打開工程文件,選擇app的target,在Launch Screen file處選擇你的Launch Screen .xib文件。
儘量地利用Launch Screen吧。你確定不想被抓到在問設計師要一年後新出的手機平板的8x啓動圖。
麻煩事No.4:「能把這些按鈕的圖片拉長一點嗎?」
這種狀況發生的機率比你想象的要高得多。對於一張pattern image或者是有圓角的圖片,考慮到有更大的屏幕,你須要從新調整圖片的大小,以避免圖片拉伸出現失真。Natasha發佈了一篇很棒的文章來講明如何編程解決這個問題,可是咱們也能夠在Xcode 6的Asset Catalog中搞定它。順便說一下,我強烈建議你在繼續往下讀以前,看一下Natasha的文章,這樣你就能理解到底發生了什麼。免責聲明:下面的圖片等是直接從Natasha的文章中拷貝過來的。Sorry!
好了,咱們繼續。
在以前,通常用相似下面的代碼來得到可改變大小的圖片:
1
2
3
4
|
let edgeInsets = UIEdgeInsets(top: 8.0, left: 8.0, bottom: 8.0, right: 8.0)
let backgroundButtonImage = UIImage(named:
"purple_button"
)?.resizableImageWithCapInsets(edgeInsets)
purpleButton.setBackgroundImage(backgroundButtonImage, forState: .Normal)
|
這將會獲得一張和下面相似的圖片:
在運行時,會拉伸距離UIImageView的container的邊框8像素的中間部分,這樣就能保留圓角,獲得下面這樣的:
多虧了Xcode中Asset Catalog的slice和dice,咱們不須要代碼也能拉伸圖片。首先在Xcode中選中圖片,而後點擊右下角的Show Slicing:
你如今應該能看到slicing 面板和一個按鈕"Start Slicing"。
在你點擊按鈕以後,會顯示下面的三個選項:
左邊的按鈕用於horizontal edge insets,右邊的按鈕用於vertical edge insets,中間的則是兩個都有。在咱們的例子中要保留圓角,因此咱們按中間的按鈕,告訴系統咱們想要按鈕的中間在水平和垂直方向拉伸。在按下按鈕以後,就能看到一些能夠拖動的細條,這能夠設置從哪裏開始拉伸圖片。
系統會保留深紫色的區域,淺紫色的區域會被拉伸。
更厲害的是,Xcode自動找到了圓角,因此咱們不須要設置從哪裏開始拉伸圖片。最後別忘了在Attribtues pane中設置圖片是可拉伸的。
若是我是你的話,我就會嘗試並習慣這個功能。有了這個無價之寶,你就不用再在resizableImageWithCapInsets方法中填寫那些神奇的數字了,也能幫助你分離view邏輯和app邏輯。
結論
我很肯定,咱們開發者幾乎天天都還會作不少其餘事去麻煩設計師們,但至少咱們能多用用這些功能,讓他們稍微休息一下子。畢竟編程能解決一切問題,何樂而不爲呢?