用UIButton實現各種圖文結合功能
iOS的UIButton是一個非常常見而且常用的控制元件,我們一般用他來實現某個功能的提交以及選擇操作等。我們可以建立只有文字的Button,也可以建立只有圖片的Button,具體的需求要看應用設計的具體情況。有時候我們希望應用的介面元素是豐富多彩的,因此可能需要建立一個即有圖片也有文字的按鈕出來,其實UIButton是支援具有圖片和文字功能的按鈕的,這個只需要分別呼叫setTitle:forState和setImage:forSate兩個方法就可以實現具有圖片和文字功能的按鈕。但是系統預設的圖文結合的按鈕佈局是圖片在左邊而文字在右邊,而且整體水平和垂直居中,比如下面這個按鈕:
但是有的時候我們又希望圖片在右邊而文字在左邊;或者圖片在上邊而文字在下面;或者圖片在按鈕的中間而文字在圖片的下面等等,但我們又不想放棄使用按鈕這個控制元件,這時候怎麼辦? 事件總是能找到解決方法的, 有的人會先建立一個按鈕控制元件鋪在下面,而在上面分別覆蓋一個UIImageView和UILabel來實現;而有的人則乾脆在UIButton上建立一個UIImageView和UILabel兩個子檢視;而有的人則不會用UIButton來實現圖文結合的功能。 前面說的幾個方法看起來有效,也確實會解決問題,但缺點是程式碼量會增加,而且必須同時管理UIButton, UIImageView, UILabel這三個整體,如果哪天產品還希望有一個按鈕按下高亮或者按下陰影效果時,估計你就傻眼了。。。
那麼我們是否要放棄UIButton呢?答案是否定的,其實UIButton本身是可以支援各種圖文結合的,既然UIButton上能同時顯示圖片和文字,那就可以肯定的說UIButton裡面本身一定有一個UIImageView和UILabel子檢視。UIButton本身就是一個複合控制元件,他分別提供了兩個屬性:
@property(nonatomic,readonly,retain)UILabel *titleLabelNS_AVAILABLE_IOS(3_0);
@property(nonatomic,readonly,retain)UIImageView *imageView
需要注意的是這兩個屬性必須要呼叫完setTitle:forSate和setImage:forSate後才能獲取到,否則有可能會返回nil。 其中的 titleLabel是用來儲存文字的而imageView是用來儲存圖片的。那既然UIButton本身就帶有一個圖片控制元件和文字控制元件,那是不是我們只要分別通過調整子控制元件的frame值就能實現我們想要的圖片文字的任何佈局呢? 答案是否定的。實驗證明通過設定titleLabel,imageView的frame值根本不會改變按鈕裡面圖片在左而文字在右的格局。 難道我們就要此放棄了嗎?其實不用。在UIButton裡面還有另外兩個屬性:
@property(nonatomic) UIEdgeInsets titleEdgeInsets; // default is UIEdgeInsetsZero
@property(nonatomic) UIEdgeInsets imageEdgeInsets; // default is UIEdgeInsetsZero
這兩個屬性是分別用來調整按鈕中文字的偏移縮排以及圖片的偏移縮排的,他們都是一個UIEdgeInsets物件,預設的值都是0,也就是預設的值都是0的情況下按鈕的圖片和文字垂直整體在按鈕中居中,而且圖片在左邊文字在右邊,而且圖片文字整體水平居中。而我們則可以通過調整titleEdgeInsets和imageEdgeInsets的值來實現我們想要的任何圖文佈局的情況,甚至我們希望圖片和文字之間還要保留一些間隙的情況。怎麼調整? 調整多少為最合適?在調整之前我們先定義幾個特定的值:
CGRect titleRect = titleLabel.frame; //文字控制元件在按鈕中的frame值。
CGRect imageRect = imageView.frame; //圖片控制元件在按鈕中的frame值。
CGFloat padding; //用於指定文字和圖片的間隙值。
CGFloat selfWidth; //按鈕控制元件的寬度
CGFloat selfHeight; //按鈕控制元件的高度
CGFloat totalHeight=titleRect.size.height+padding+imageRect.size.height; //圖文上下佈局時所佔用的總高度,注意這裡也算上他們之間的間隙值padding
同時還需要注意的是我們只是調整文字和檢視的位置,並不會調整大小,如果我們想往右移動20的話,那麼就應該設定left=20, right=-20,而如果我們想往上移動20的話,那麼就應該設定top=-20,bottom=20
一、圖片在左,文字在右 保持整體居中
這種方式是按鈕本來的圖文佈局方式,因為要設定圖片和文字的間距,所以只需要文字右移padding/2而圖片左移padding/2值就可以了。公式為:
titleEdgeInsets =UIEdgeInsetsMake(0,
padding/2,
0,
-padding/2);
imageEdgeInsets = UIEdgeInsetsMake(0,
-padding/2,
0,
padding/2);
二、圖片在右,文字在左 保持整體居中
這種方式下我們只需要將文字往左偏移圖片的寬度並且再往左偏移padding/2就可以了,而圖片則只需要往右偏移文字的寬度並再往右偏移padding/2就可以了。公式為:
titleEdgeInsets =UIEdgeInsetsMake(0,
-(imageRect.size.width + padding/2),
0,
(imageRect.size.width + padding/2));
imageEdgeInsets =UIEdgeInsetsMake(0,
(titleRect.size.width+ padding/2),
0,
-(titleRect.size.width+ padding/2));
三、圖片在上,文字在下 保持整體居中
這種方式下當圖片和文字要求垂直居中後,新的圖片的頂部位置應該等於(selfHeight - totalHeight)/2, 因此垂直需要偏移的值就是新的位置減去原來的位置imageRect.origin.y;而新的圖片的水平中心點要等於selfWidth/2,而原來的圖片的水平中心點等於imageRect.origin.x + imageRect.size.width/2,兩者相減就是水平需要偏移的值。而新的文字的頂部位置應該等於新的圖片的頂部位置(selfHeight - totalHeight)/2 + 圖片的高度imageRect.size.height + 間隙padding ,因此垂直需要偏移的值就是新的頂部值減去原來的頂部位置titleRect.origin.y; 而新的文字的水平中心點也是selfWidth/2,而原來的文字的水平中心點是titleRect.origin.x + titleRect.size.width/2, 兩者相減就是水平需要偏移的值,又因為預設的情況下當按鈕比較小時會自動保留圖片的尺寸和將文字部分縮小,因為當我們實現文字和圖片上下佈局時,需要將文字的區域擴充套件到整個按鈕部分,否則將會縮小按鈕的文字的寬度,因為按鈕的寬度為selfWidth,而文字的預設寬度是titleRect.size.width,所以上面的實現將文字移到中間後還需要分別向兩邊進行拉伸(selfWith - titleRect.size.width)/2來保證文字填充滿所有的按鈕區域,在下面的各種樣式中凡是文字和圖片垂直居中的情況下都要考慮這種情況 公式為:
titleEdgeInsets =UIEdgeInsetsMake(((selfHeight - totalHeight)/2 + imageRect.size.height + padding - titleRect.origin.y),
(selfWidth/2 - titleRect.origin.x - titleRect.size.width /2) - (selfWidth - titleRect.size.width) /2,
-((selfHeight - totalHeight)/2 + imageRect.size.height + padding - titleRect.origin.y),
-(selfWidth/2 - titleRect.origin.x - titleRect.size.width /2) - (selfWidth - titleRect.size.width) /2);
imageEdgeInsets =UIEdgeInsetsMake(((selfHeight - totalHeight)/2 - imageRect.origin.y),
(selfWidth /2 - imageRect.origin.x - imageRect.size.width /2),
-((selfHeight - totalHeight)/2 - imageRect.origin.y),
-(selfWidth /2 - imageRect.origin.x - imageRect.size.width /2));
四、圖片在下,文字在上 保持整體居中
這種方式就是方式三文字和圖片位置調換,因此公式為:
titleEdgeInsets =UIEdgeInsetsMake(((selfHeight - totalHeight)/2 - titleRect.origin.y),
(selfWidth/2 - titleRect.origin.x - titleRect.size.width /2) - (selfWidth - titleRect.size.width) /2,
-((selfHeight - totalHeight)/2 - titleRect.origin.y),
-(selfWidth/2 - titleRect.origin.x - titleRect.size.width /2) - (selfWidth - titleRect.size.width) /2);
imageEdgeInsets =UIEdgeInsetsMake(((selfHeight - totalHeight)/2 + titleRect.size.height + padding - imageRect.origin.y),
(selfWidth /2 - imageRect.origin.x - imageRect.size.width /2),
-((selfHeight - totalHeight)/2 + titleRect.size.height + padding - imageRect.origin.y),
-(selfWidth /2 - imageRect.origin.x - imageRect.size.width /2));
五、圖片保持居中,而文字左右居中,頂部距離按鈕頂部
這種方式要求圖片在按鈕居中,而文字則要求左右居中而垂直方向位置則是距離按鈕頂部的間隙值。 上面因為描述了水平居中的調整,因此這裡就不介紹了,只介紹垂直方向的調整。 因為要求圖片要垂直居中,因此不需要調整垂直偏移。而文字要調整為距離頂部的間隙值,也就是新的文字的頂部值等於padding, 而原來頂部值是titleRect.origin.y,因此只需要偏移titleRect.origin.y - padding就可以了。公式為:
titleEdgeInsets =UIEdgeInsetsMake(-(titleRect.origin.y - padding),
(selfWidth /2 - titleRect.origin.x - titleRect.size.width /2) - (selfWidth - titleRect.size.width) /2,
(titleRect.origin.y - padding),
-(selfWidth /2 - titleRect.origin.x - titleRect.size.width /2) - (selfWidth - titleRect.size.width) /2);
imageEdgeInsets =UIEdgeInsetsMake(0,
(selfWidth /2 - imageRect.origin.x - imageRect.size.width /2),
0,
-(selfWidth /2 - imageRect.origin.x - imageRect.size.width /2));
六、圖片保持居中,而文字水平居中,底部距離按鈕底部
這種方式要求圖片在按鈕居中,而文字則要求左右居中而垂直方向的底部位置則是距離按鈕底部的間隙值。圖片的調整上面有介紹,而文字的水平調整上面也有說到,這裡面只說文字的垂直調整。文字新的底部位置等於 selfHeight - padding, 而舊的底部位置是titleRect.size.height + titleRect.origin.y, 因此要偏移的位置就是兩者相減的值。具體的公式為:
titleEdgeInsets =UIEdgeInsetsMake((selfHeight - padding - titleRect.origin.y - titleRect.size.height),
(selfWidth /2 - titleRect.origin.x - titleRect.size.width /2) - (selfWidth - titleRect.size.width) /2,
-(selfHeight - padding - titleRect.origin.y - titleRect.size.height),
-(selfWidth /2 - titleRect.origin.x - titleRect.size.width /2) - (selfWidth - titleRect.size.width) /2);
imageEdgeInsets =UIEdgeInsetsMake(0,
(selfWidth /2 - imageRect.origin.x - imageRect.size.width /2),
0,
-(selfWidth /2 - imageRect.origin.x - imageRect.size.width /2));
七、圖片保持居中,而文字水平居中,並且在圖片的上面
這種方式要求圖片在按鈕居中,而文字則要求左右居中並且在垂直在圖片的上面並保留出padding的間隙。 圖片的偏移上面有說到,而文字的水平偏移上面也有說到,這裡只說垂直偏移,文字新的底部位置等於圖片的頂部位置 - padding 而文字老的底部位置等於titleRect.size.height + titleRect.origin.y, 因此兩者的差值就是文字需要垂直偏移的值。具體的公式為:
titleEdgeInsets =UIEdgeInsetsMake(-(titleRect.origin.y + titleRect.size.height - imageRect.origin.y + padding),
(selfWidth /2 - titleRect.origin.x - titleRect.size.width /2) - (selfWidth - titleRect.size.width) /2,
(titleRect.origin.y + titleRect.size.height - imageRect.origin.y + padding),
-(selfWidth /2 - titleRect.origin.x - titleRect.size.width /2) - (selfWidth - titleRect.size.width) /2);
imageEdgeInsets =UIEdgeInsetsMake(0,
(selfWidth /2 - imageRect.origin.x - imageRect.size.width /2),
0,
-(selfWidth /2 - imageRect.origin.x - imageRect.size.width /2));
八、圖片保持居中,而文字水平居中,並且在圖片的下面
這種方式要求圖片在按鈕居中,而文字則要求左右居中並且垂直在圖片的下面並保留出padding的間隙。圖片的偏移上面有說到,而文字的水平偏移上面也有說到,這裡只說垂直偏移,文字新的頂部位置等於imageRect.origin.y + imageRect.size.height + padding, 而文字老的頂部位置等於titleRect.origin.y,因此兩者的差值就是文字需要垂直偏移的值。具體的公式為:
titleEdgeInsets =UIEdgeInsetsMake((imageRect.origin.y + imageRect.size.height - titleRect.origin.y + padding),
(selfWidth /2 - titleRect.origin.x - titleRect.size.width /2) - (selfWidth - titleRect.size.width) /2,
-(imageRect.origin.y + imageRect.size.height - titleRect.origin.y + padding),
-(selfWidth /2 - titleRect.origin.x - titleRect.size.width /2) - (selfWidth - titleRect.size.width) /2);
imageEdgeInsets =UIEdgeInsetsMake(0,
(selfWidth /2 - imageRect.origin.x - imageRect.size.width /2),
0,
-(selfWidth /2 - imageRect.origin.x - imageRect.size.width /2));
好了說了那麼多的圖文結合樣式,我想應該可以滿足您的需求了,如果這些圖文結合的樣式還是無法滿足您的需求時則您還是別用UIButton了。
為了方便大家的使用,我把上面的圖文結合樣式整理成了一個UIButton的分類方法,大家可以直接拷貝使用了,要注意的是設定這個方法之前需要確定按鈕的frame是已經被計算出來了:
標頭檔案:
//
// UIButton+ImageTitleStyle.h
//
// Created by 歐陽大哥 on 14-7-13.
//
#import <UIKit/UIKit.h>
/*
針對同時設定了Image和Title的場景時UIButton中的圖片和文字的關係
*/
typedef NS_ENUM(NSInteger, ButtonImageTitleStyle ) {
ButtonImageTitleStyleDefault = 0, //圖片在左,文字在右,整體居中。
ButtonImageTitleStyleLeft = 0, //圖片在左,文字在右,整體居中。
ButtonImageTitleStyleRight = 2, //圖片在右,文字在左,整體居中。
ButtonImageTitleStyleTop = 3, //圖片在上,文字在下,整體居中。
ButtonImageTitleStyleBottom = 4, //圖片在下,文字在上,整體居中。
ButtonImageTitleStyleCenterTop = 5, //圖片居中,文字在上距離按鈕頂部。
ButtonImageTitleStyleCenterBottom = 6, //圖片居中,文字在下距離按鈕底部。
ButtonImageTitleStyleCenterUp = 7, //圖片居中,文字在圖片上面。
ButtonImageTitleStyleCenterDown = 8, //圖片居中,文字在圖片下面。
};
@interface UIButton (ImageTitleStyle)
/*
調整按鈕的文字和image的佈局,前提是title和image同時存在才會調整。
padding是調整佈局時整個按鈕和圖文的間隙。
*/
-(void)setButtonImageTitleStyle:(ButtonImageTitleStyle)style padding:(CGFloat)padding;
@end
實現檔案:
@implementation UIButton (ImageTitleStyle)
-(void)setButtonImageTitleStyle:(ButtonImageTitleStyle)style padding:(CGFloat)padding
{
if (self.imageView.image != nil && self.titleLabel.text != nil)
{
//先還原
self.titleEdgeInsets = UIEdgeInsetsZero;
self.imageEdgeInsets = UIEdgeInsetsZero;
CGRect imageRect = self.imageView.frame;
CGRect titleRect = self.titleLabel.frame;
CGFloat totalHeight = imageRect.size.height + padding + titleRect.size.height;
CGFloat selfHeight = self.frame.size.height;
CGFloat selfWidth = self.frame.size.width;
switch (style) {
case ButtonImageTitleStyleLeft:
if (padding != 0)
{
self.titleEdgeInsets = UIEdgeInsetsMake(0,
padding/2,
0,
-padding/2);
self.imageEdgeInsets = UIEdgeInsetsMake(0,
-padding/2,
0,
padding/2);
}
break;
case ButtonImageTitleStyleRight:
{
//圖片在右,文字在左
self.titleEdgeInsets = UIEdgeInsetsMake(0,
-(imageRect.size.width + padding/2),
0,
(imageRect.size.width + padding/2));
self.imageEdgeInsets = UIEdgeInsetsMake(0,
(titleRect.size.width+ padding/2),
0,
-(titleRect.size.width+ padding/2));
}
break;
case ButtonImageTitleStyleTop:
{
//圖片在上,文字在下
self.titleEdgeInsets = UIEdgeInsetsMake(((selfHeight - totalHeight)/2 + imageRect.size.height + padding - titleRect.origin.y),
(selfWidth/2 - titleRect.origin.x - titleRect.size.width /2) - (selfWidth - titleRect.size.width) / 2,
-((selfHeight - totalHeight)/2 + imageRect.size.height + padding - titleRect.origin.y),
-(selfWidth/2 - titleRect.origin.x - titleRect.size.width /2) - (selfWidth - titleRect.size.width) / 2);
self.imageEdgeInsets = UIEdgeInsetsMake(((selfHeight - totalHeight)/2 - imageRect.origin.y),
(selfWidth /2 - imageRect.origin.x - imageRect.size.width / 2),
-((selfHeight - totalHeight)/2 - imageRect.origin.y),
-(selfWidth /2 - imageRect.origin.x - imageRect.size.width / 2));
}
break;
case ButtonImageTitleStyleBottom:
{
//圖片在下,文字在上。
self.titleEdgeInsets = UIEdgeInsetsMake(((selfHeight - totalHeight)/2 - titleRect.origin.y),
(selfWidth/2 - titleRect.origin.x - titleRect.size.width / 2) - (selfWidth - titleRect.size.width) / 2,
-((selfHeight - totalHeight)/2 - titleRect.origin.y),
-(selfWidth/2 - titleRect.origin.x - titleRect.size.width / 2) - (selfWidth - titleRect.size.width) / 2);
self.imageEdgeInsets = UIEdgeInsetsMake(((selfHeight - totalHeight)/2 + titleRect.size.height + padding - imageRect.origin.y),
(selfWidth /2 - imageRect.origin.x - imageRect.size.width / 2),
-((selfHeight - totalHeight)/2 + titleRect.size.height + padding - imageRect.origin.y),
-(selfWidth /2 - imageRect.origin.x - imageRect.size.width / 2));
}
break;
case ButtonImageTitleStyleCenterTop:
{
self.titleEdgeInsets = UIEdgeInsetsMake(-(titleRect.origin.y - padding),
(selfWidth / 2 - titleRect.origin.x - titleRect.size.width / 2) - (selfWidth - titleRect.size.width) / 2,
(titleRect.origin.y - padding),
-(selfWidth / 2 - titleRect.origin.x - titleRect.size.width / 2) - (selfWidth - titleRect.size.width) / 2);
self.imageEdgeInsets = UIEdgeInsetsMake(0,
(selfWidth / 2 - imageRect.origin.x - imageRect.size.width / 2),
0,
-(selfWidth / 2 - imageRect.origin.x - imageRect.size.width / 2));
}
break;
case ButtonImageTitleStyleCenterBottom:
{
self.titleEdgeInsets = UIEdgeInsetsMake((selfHeight - padding - titleRect.origin.y - titleRect.size.height),
(selfWidth / 2 - titleRect.origin.x - titleRect.size.width / 2) - (selfWidth - titleRect.size.width) / 2,
-(selfHeight - padding - titleRect.origin.y - titleRect.size.height),
-(selfWidth / 2 - titleRect.origin.x - titleRect.size.width / 2) - (selfWidth - titleRect.size.width) / 2);
self.imageEdgeInsets = UIEdgeInsetsMake(0,
(selfWidth / 2 - imageRect.origin.x - imageRect.size.width / 2),
0,
-(selfWidth / 2 - imageRect.origin.x - imageRect.size.width / 2));
}
break;
case ButtonImageTitleStyleCenterUp:
{
self.titleEdgeInsets = UIEdgeInsetsMake(-(titleRect.origin.y + titleRect.size.height - imageRect.origin.y + padding),
(selfWidth / 2 - titleRect.origin.x - titleRect.size.width / 2) - (selfWidth - titleRect.size.width) / 2,
(titleRect.origin.y + titleRect.size.height - imageRect.origin.y + padding),
-(selfWidth / 2 - titleRect.origin.x - titleRect.size.width / 2) - (selfWidth - titleRect.size.width) / 2);
self.imageEdgeInsets = UIEdgeInsetsMake(0,
(selfWidth / 2 - imageRect.origin.x - imageRect.size.width / 2),
0,
-(selfWidth / 2 - imageRect.origin.x - imageRect.size.width / 2));
}
break;
case ButtonImageTitleStyleCenterDown:
{
self.titleEdgeInsets = UIEdgeInsetsMake((imageRect.origin.y + imageRect.size.height - titleRect.origin.y + padding),
(selfWidth / 2 - titleRect.origin.x - titleRect.size.width / 2) - (selfWidth - titleRect.size.width) / 2,
-(imageRect.origin.y + imageRect.size.height - titleRect.origin.y + padding),
-(selfWidth / 2 - titleRect.origin.x - titleRect.size.width / 2) - (selfWidth - titleRect.size.width) / 2);
self.imageEdgeInsets = UIEdgeInsetsMake(0,
(selfWidth / 2 - imageRect.origin.x - imageRect.size.width / 2),
0,
-(selfWidth / 2 - imageRect.origin.x - imageRect.size.width / 2));
}
break;
case ButtonImageTitleStyleRightLeft:
{
//圖片在右,文字在左,距離按鈕兩邊邊距
self.titleEdgeInsets = UIEdgeInsetsMake(0,
-(titleRect.origin.x - padding),
0,
(titleRect.origin.x - padding));
self.imageEdgeInsets = UIEdgeInsetsMake(0,
(selfWidth - padding - imageRect.origin.x - imageRect.size.width),
0,
-(selfWidth - padding - imageRect.origin.x - imageRect.size.width));
}
break;
case ButtonImageTitleStyleLeftRight:
{
//圖片在左,文字在右,距離按鈕兩邊邊距
self.titleEdgeInsets = UIEdgeInsetsMake(0,
(selfWidth - padding - titleRect.origin.x - titleRect.size.width),
0,
-(selfWidth - padding - titleRect.origin.x - titleRect.size.width));
self.imageEdgeInsets = UIEdgeInsetsMake(0,
-(imageRect.origin.x - padding),
0,
(imageRect.origin.x - padding));
}
break;
case ButtonImageTitleStyleDefault: {
self.titleEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0);
self.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0);
}
break;
default:
break;
}
}
}
@end