【IOS】自己動手豐衣足食--圖片篇

weixin_34353714發表於2017-07-18

想象你正在幹活,你的上級要求你在工程中加入一些圖片。你找到團隊中的設計師(如果你們有一個的話),鼓起勇氣,問他要這些圖片。不過,通常他都會忙得根本沒時間幫你。因為他要做的活兒比你還多。說實話,你到底見過一個設計師有多少活兒要做嗎?!簡直多到令人髮指啊!所以你最不想做的就是成為那個再給他加活兒的人了。特別是那些活兒對設計師來說簡單得讓人厭煩。更不用說,設計師也要按順序幹活,你拿到那些圖片也是幾天之後了。所以我們還是來看看如何用Xcode 的Asset Catalog來處理這些麻煩事吧。

746057-ca1abe083efe1586.png

麻煩事No.1:“能改一下這個圖片的顏色嗎?”

目前為止iOS已經提供了一些相當複雜的方法來處理工程中的圖片,但很不幸的是,卻沒有用來處理這種情況的方法。我已經數不清楚有多少次我們因為什麼便捷性支援或者是客戶不喜歡而改變工程的顏色了。現在在工程中全域性改變UIColor已經不是什麼難題了,但是我們還要改變工程中圖片的顏色。這樣我們就不得不回去找已經忙得要死的設計師了,就因為我們太懶而不去學學怎麼用Photoshop。當然我們也可以寫程式碼來完成,不過看起來相當複雜,而且還容易造成洩漏。在iOS7 之前,寫程式碼改變圖片的顏色和下面差不多(還有很多其他的方法):

//改變圖片顏色
- (UIImage *)imageWithColor:(UIColor *)color
{
UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale);
CGContextRefcontext = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, 0, self.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
CGContextSetBlendMode(context, kCGBlendModeNormal);
    CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
    CGContextClipToMask(context, rect, self.CGImage);
    [color setFill];
    CGContextFillRect(context, rect);
UIImage*newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
    return newImage;
}

在兩個大版本升級之後,我還能看到這樣的程式碼。在iOS7中,我們有了imageWithRenderingMode,這是UIImage的一個方法,引數是有三個選項的列舉值UIImageRenderingMode

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渲染模式。

在瞭解了上面的內容之後,之前我們改變圖片顏色的程式碼就可以簡化成下面這樣的了:

UIImage *theImage;
[theImage  imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
imageView.image = theImage;
imageView.tintColor = [UIColor color....]; 

是不是很神奇?用程式碼改變圖片的顏色,現在變得簡單多了。

等等,還沒完呢!其實不需要程式碼也可以改變圖片的顏色。

從Xcode 6開始,imageWithRenderingMode
已經整合到Asset Catalog裡了。如果你在Asset Catalog裡選擇了一個圖片,在右邊的Attributes Inspector裡,就可以像下圖那樣把Render As選項改成Template Image。

746057-11e40432613eb091.png

就是這麼簡單。甚至你還可以通過在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就行了。

746057-c7b91de837e4b88a

你可以直接把PDF拖到Asset Catalog中,然後進行設定。

麻煩事No.3:“能給我新裝置的啟動圖嗎?”

xcode8之後,已經變成預設的方式了.

啟動圖對於app來說還是蠻重要的。這是啟動app後最先看到的,它會給使用者一個app其餘部分是如何設計的第一印象。如果我看到一個設計得很糟糕的啟動圖,我會認為app其他地方也好不到哪去,當然這只是我的情況。對於我們那可憐的設計師來說,每次有新裝置出來時,他們都知道要放大啟動圖來支援新裝置的解析度。對於iPhone 6 和 iPhone 6 Plus,如果你沒有為這兩個裝置準備對應的啟動圖,那麼app就會工作在放大模式。啟動圖還在Asset Catalog中,但是我建議把它拆出來,因為啟動圖也升級了。現在,你可以使用LaunchScreen xibs。

在工程檔案中,你可以指定app在啟動時載入的xib,這樣你就不需要準備9張啟動圖了。LaunchScreen.xib還支援自動佈局,這樣我們就能分塊構建啟動螢幕了。按如下這樣設定:

首先建立一個xib檔案。你可以在如下圖所示的地方選擇Launch Screen型別的xib。

746057-58932a63ac3a19f0

然後開啟工程檔案,選擇app的target,在Launch Screen file處選擇你的Launch Screen .xib檔案。

746057-fb30e487888b563d

儘可能地利用Launch Screen吧。你肯定不想被抓到在問設計師要一年後新出的手機平板的8x啟動圖。

麻煩事No.4:“能把這些按鈕的圖片拉長一點嗎?”
這種情況發生的概率比你想象的要高得多。對於一張pattern image或者是有圓角的圖片,考慮到有更大的螢幕,你需要重新調整圖片的大小,以免圖片拉伸出現失真。Natasha釋出了一篇很棒的文章來說明如何程式設計解決這個問題,但是我們也可以在Xcode 6的Asset Catalog中搞定它。順便說一下,我強烈建議你在繼續往下讀之前,看一下Natasha的文章,這樣你就能理解到底發生了什麼。免責宣告:下面的圖片等是直接從Natasha的文章中拷貝過來的。Sorry!

好了,我們繼續。

在之前,一般用類似下面的程式碼來獲得可改變大小的圖片:

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)​​

這將會得到一張和下面類似的圖片:

746057-f63f6caba184bc2e

在執行時,會拉伸距離UIImageView的container的邊框8畫素的中間部分,這樣就能保留圓角,得到下面這樣的:

746057-10a57e6c74ab5e48

多虧了Xcode中Asset Catalog的slice和dice,我們不需要程式碼也能拉伸圖片。首先在Xcode中選中圖片,然後點選右下角的Show Slicing

746057-52f6bb34b5eabcd5

你現在應該能看到slicing 皮膚和一個按鈕"Start Slicing"。

746057-1785254e10b79e08

在你點選按鈕之後,會顯示下面的三個選項:

746057-758628e632ef59f4

左邊的按鈕用於horizontal edge insets,右邊的按鈕用於vertical edge insets,中間的則是兩個都有。在我們的例子中要保留圓角,所以我們按中間的按鈕,告訴系統我們想要按鈕的中間在水平和垂直方向拉伸。在按下按鈕之後,就能看到一些可以拖動的細條,這可以設定從哪裡開始拉伸圖片。

746057-20582ce4b3fd65f0

系統會保留深紫色的區域,淺紫色的區域會被拉伸。

更厲害的是,Xcode自動找到了圓角,所以我們不需要設定從哪裡開始拉伸圖片。最後別忘了在Attribtues pane中設定圖片是可拉伸的。

746057-1a1db8d1787f7d94.jpg

如果我是你的話,我就會嘗試並習慣這個功能。有了這個無價之寶,你就不用再在resizableImageWithCapInsets
方法中填寫那些神奇的數字了,也能幫助你分離view邏輯和app邏輯。

結論
我很確定,我們開發者幾乎每天都還會做很多其他事去麻煩設計師們,但至少我們能多用用這些功能,讓他們稍微休息一會兒。畢竟程式設計能解決一切問題,何樂而不為呢?

相關文章