[iOS] 設定UIButton的圖片與文字的作法

這邊要紀錄整套設定UIButton的image和title的作法。

resizableImageWithCapInsets的使用

首先對於調整固定大小的圖片進行拉伸而不影響corner的形狀,通常是針對UIButton的background的圖片進行調整,可以參考官方文件對於EdgeInsets的介紹:

// btn是UIButton
UIImage *imgBG = [UIImage imageNamed:@"btn_bg.png"];

imgBG = [imgBG resizableImageWithCapInsets:UIEdgeInsetsMake(imgBG.size.height*.4f, imgBG.size.width*.4f, imgBG.size.height*.4f, imgBG.size.width*.4f) resizingMode:UIImageResizingModeStretch];

[btn setBackgroundImage:imgBG forState:UIControlStateNormal];

第一行是建立一個UIImage的實例,名為imgBG,等一下要作為背景。第二行是設定他的EdgeInsets,意思是由按鈕的中心點開始,往上、往左、往下和往右的距離是寬或高的40%,這樣的話,可變尺寸區域為寬度*0.2 X 高度*0.2。注意,resizableImageWithCapInsets會回傳一個新的UIImage。

依照官方文件,渲染的作法有以下三種:
  1. 如果寬或高度只有1個pixel,亦即,可改變尺寸的水平或垂直長度為1個pixel,那麼會將這個區域延展拉伸,效能最好,適合單一顏色的圖片。
  2. 若可變尺寸的水平或垂直長度大於1個pixel,那這塊區域會被重複排列以作為拉伸的結果,比方說你設定的可變尺寸的區域為10X10,而將拉伸的大小是100X50,那就會排列出50個這樣的區塊,這種作法對於材質類型的圖片特別好用,效能比上面的差。
  3. 如果是整塊圖片都可以延展,亦即,UIEdgeInsentsZero,那麼整塊區域都會拿來重複排列(跟2的結果一樣),但效能比2好一些。
如果你希望你指定的可變尺寸區域是延展的,那可以用這個方法,也就是我上面用的resizableImageWithCapInsets:resizingMode:,第二個參數可以指定是tile或stretch。

Alignment和contentMode的使用

再來是設定image的部份,我之所以不用以上的方法去設定,是因為我的image的圖片裡面有一個圖案,我找不到一個適當的作法讓它正常的延展:

btn.imageView.contentMode = UIViewContentModeScaleAspectFit;
btn.contentVerticalAlignment = UIControlContentVerticalAlignmentFill;
btn.contentHorizontalAlignment = UIControlContentHorizontalAlignmentFill;

首先針對按鈕的imageView的contentMode進行設定,你也可以設定為UIViewContentModeScaleAspectFill,只是如果超出去的部份你要多設定clip去裁切。第二第三行是針對垂直與水平部分的效果去設定,我的圖片主要是高度要符合水平伸展的按鈕的高度,只是如果不加設水平部分,圖片不會有任何改變。而如果不設定contentMode,以我的圖片來說,比例會跑掉。

EdgeInsets的使用

這樣設定完之後,文字會和圖片重疊到,這應該是圖片延展了但是title和image之間依舊使用舊的insets的設定的關係,所以要去設定文字的insent:

[btn setTitleEdgeInsets:UIEdgeInsetsMake(0, btn.imageView.image.size.width, 0, 0)];

主要是將文字與右邊bound的距離加大,這邊加大了一個按鈕圖片尺寸寬度的距離。需要注意的是,文字在往左移動時,要以左邊為主,不然可能會被裁切掉,就是你要設定文字的左邊去移動,就是在left的參數中輸入負值,而不是在right的參數輸入正值加大右側的邊界距離,這樣左邊邊界沒移動,文字的特性會讓文字顯示的結果看似被裁切掉。

另外補充一點,按鈕的EdgeInsents有三種:
  1. Content:包含title和image,表示實際的內容,有些按鈕的背景圖片或許會有不一樣的實際內容範圍,比方說line的氣泡對話框,多出去的尖尖的形狀也是整個背景的一部分,但如果將之計入的話,文字會無法好好的放在對話框內,所以這可以調整Content的實際範圍。
  2. Title:文字的部份
  3. Image:圖片的部份
操作心得

操作了一陣子,紀錄一個簡單的範例作為心得。

假設你的按鈕裡面有圖片也有文字,然後按鈕是狹長型,水平方向比較長,那你的按鈕長相會是圖片在文字的左邊。

那如果你要把圖片放到文字右邊,然後文字置中,那要怎麼修改呢?

首先是文字的部份,文字被圖片擠壓的關係,所以往右偏了一個圖片寬,所以在他的inset裡面,要在右邊的inset欄位裡面移動一個圖片寬,這樣文字就會剛好在中間。所以說文字會被圖片擠壓而往右移動,這是可以理解和接受的。

再來是圖片的部份,為了順應不同的文字長度,你會希望圖片在文字的右邊,相對距離則是隨著文字長度作移動,那你就要取得文字的寬度(這一點你要注意,如果直接取用按鈕的titleLabel,他的frame可能是0,所以最好是尋找可以直接計算text長度的方法,可以參考這連結),隨後要進行操作。

首先,你先想像文字已經移到中間,你的圖片原點實際上是在整個按鈕的原點,你要移動這原點,剛好移動到文字右邊的話,你要先右移一半的按鈕寬度,這時你的圖片會在文字中間也在按鈕中間,然後你再右移一半的文字寬度,這樣剛好就是在文字的右邊了。

結論是,你要移動一半的按鈕寬再加上一半的文字寬,這個直放在圖片的inset裡面代表左側的欄位裡面。

所以圖片和文字兩者的原點會互有影響,要正確理解兩者的原點會怎麼排放,這樣才好正確操作edge inset,在你們操作自己按鈕的圖片與文字之前,最好先搞懂他們之間的相互關係,這樣修改起來才會比較快。

留言