前言
平常開發(fā)中玷禽,UIButton是使用頻率非常高的控件。除了可點擊之外呀打,還因為其能夠同時顯示文案和圖片矢赁。默認(rèn)的UIButton,圖片在左贬丛,文字在右撩银,且文字是緊緊挨著圖片的。大多數(shù)情況下豺憔,UIButton已經(jīng)可以滿足需求了额获,然而够庙,總有一些意外……比如,設(shè)計讓文字和圖片不要離得很近抄邀;比如首启,設(shè)計讓文字在左,圖片在右撤摸;比如毅桃,設(shè)計讓圖片在上,文字在下……
這時候就會用到本文的主角:imageEdgeInsets和titleEdgeInsets了准夷。使用imageEdgeInsets和titleEdgeInsets時钥飞,可能或多或少的都會有一種感覺,就是難以捉摸衫嵌,每次都是嘗試多次才能達(dá)到一個良好的效果读宙。再徹底會使用imageEdgeInsets和titleEdgeInsets之前,我們先來分析下面對的問題楔绞。
圖片和文字保持間距
首先面臨的第一個問題就是圖片和文字之間保持一個距離结闸。其實碰到這樣的需求解決方法有很多,如果對imageEdgeInsets和titleEdgeInsets不熟悉酒朵,完全沒必要使用這兩個屬性桦锄。
圖片右側(cè)透明
UIButton 默認(rèn)圖片在左,文字在右蔫耽,且文字和圖片緊鄰结耀,現(xiàn)在的需求是圖片和文字之間留有一定的間距。一種方法是匙铡,如果設(shè)計提供的圖片右側(cè)有一些透明的地方图甜,那么整體給用戶的感覺就是圖片和文字之間有一定的間距。只不過這種方法需要讓設(shè)計切圖鳖眼。
文案加空格
其實如果只是想讓圖片和文字之間有一定間距黑毅,不用設(shè)計切圖,代碼完全可以控制钦讳,而且特別簡單矿瘦。方法就是在文案的前面加空格,比如說本來要顯示的文案是"聯(lián)系我們",程序中可以設(shè)置為"????? 聯(lián)系我們",這樣最終展示給用戶的效果圖片和文件之間也有一定的間距蜂厅。這種方法完全是開發(fā)人員可控的匪凡。
圖片和文字位置調(diào)整
圖片和文字的位置調(diào)整膊畴,主要是指圖片在右掘猿,文字在左,或者圖片在上唇跨,文字在下這兩種情況稠通,目前還沒有碰到過文字在上衬衬,圖片在下的需求。如果設(shè)計的圖是圖片和文字的位置有了調(diào)整改橘,那么僅僅靠增加圖片透明度滋尉、文案加空格等類似的方法是滿足不了需求的。
解決方法有兩種飞主,一種是使用UIView狮惜,另外就是使用imageEdgeInsets和titleEdgeInsets。
使用UIView解決
使用UIView的思路非常簡單碌识。就是使用三個控件碾篡,一個UIView作為父控件,一個UILabel用來顯示內(nèi)容筏餐,一個UIImageView用來顯示圖片开泽。由于UILabel和UIImageView都是我們自己寫的,其frame我們可以隨意控制魁瞪,圖片是在左穆律、在右,還是在上导俘,都是可以控制的峦耘。
至于UIButton的點擊事件,因為UIView不具備點擊事件旅薄,我們可以給UIView增加手勢UITapGestureRecognizer,用來模擬點擊行為贡歧。看上去完美解決了這個問題赋秀,但是這種方案是有缺點的利朵,看一下:
首先這種方法是使用了3個控件,而如果使用UIButton猎莲,只需要使用1個控件绍弟。雖然UIButton內(nèi)部也包含了一個UIImageView和一個UILabel,性能上可能沒有太大的差距著洼,但是這種寫法麻煩啊樟遣。本來使用UIButton5行代碼就可以搞定了,結(jié)果使用UIView的形式身笤,寫了20行代碼豹悬,實現(xiàn)比較繁瑣。
雖然可以給UIView添加手勢模擬點擊行為液荸,但是瞻佛,UIView是沒有高亮狀態(tài)的。UIButton默認(rèn)是有普通狀態(tài)和高亮狀態(tài),點擊時會顯示默認(rèn)的高亮狀態(tài)伤柄,這個效果UIView是實現(xiàn)不了的绊困。如果想實現(xiàn),需要再增加更多的代碼适刀。
可以說秤朗,使用UIView能夠解決問題,但是解決方法不夠優(yōu)雅笔喉。
imageEdgeInsets和titleEdgeInsets
蘋果可能已經(jīng)考慮到了開發(fā)者會有這樣的需求取视,于是提供了imageEdgeInsets和titleEdgeInsets兩個屬性。然而常挚,由于蘋果沒有介紹這兩個屬性的原理贫途,對兩個屬性的描述又不是特別清晰,導(dǎo)致使用起來難度較大待侵,對于經(jīng)驗不足的開發(fā)者丢早,每次使用都要嘗試多次。
先來看下兩者的定義秧倾。
imageEdgeInsets和titleEdgeInsets的定義
imageEdgeInsets和titleEdgeInsets的定義在UIButton.h中怨酝,如下:
@property(nonatomic) UIEdgeInsets titleEdgeInsets; // default is UIEdgeInsetsZero
@property(nonatomic) UIEdgeInsets imageEdgeInsets; // default is UIEdgeInsetsZero
可以看到,imageEdgeInsets和titleEdgeInsets都是UIEdgeInsets類型那先,且默認(rèn)取值是UIEdgeInsetsZero农猬。看一下UIEdgeInsets的定義:
typedef struct UIEdgeInsets {
CGFloat top, left, bottom, right; // specify amount to inset (positive) for each of the edges. values can be negative to 'outset'
} UIEdgeInsets;
UIEdgeInsets是一個結(jié)構(gòu)體售淡,四個值分別是上斤葱、左、下揖闸、右揍堕,值可以為正,也可以為負(fù)汤纸。
在了解imageEdgeInsets和titleEdgeInsets的使用之前衩茸,有三點一定要明白:
無論是imageEdgeInsets還是titleEdgeInsets,都是和原控件的位置相比較的贮泞。imageEdgeInsets是和原imageView的位置比較楞慈,titleEdgeInsets是和原label的位置比較。
imageEdgeInsets和titleEdgeInsets中的值為正啃擦,則是該方向上的擴(kuò)張囊蓝,如果值為負(fù),則是該方向上的縮減令蛉。舉例來說聚霜,對于左側(cè),擴(kuò)張是更向左,即frame的x值減懈┪;對于右側(cè)擴(kuò)張是更向右运杭,frame的width值更大夫啊。
UIEdgeInsets是對稱的,左右對稱辆憔,上下對稱撇眯。因此,在設(shè)置imageEdgeInsets和titleEdgeInsets時盡量也要對稱虱咧,比如[0,-5,0,5],左右對稱熊榛,同理上下也要對稱。之所以要對稱腕巡,是為了不拉伸imageView和titleLabel玄坦。
imageEdgeInsets和titleEdgeInsets的使用
有了上面的了解和介紹,來看一下使用imageEdgeInsets和titleEdgeInsets如何解決文中最開始提到的問題绘沉。
首先是圖片和文字之間保持間距煎楣。圖片和文字保持間距有兩種處理方式,一種是圖片左移车伞,一種是文字右移择懂。實際上,最開始提到的給圖片右側(cè)增加透明度以及文案加空格正是分別對應(yīng)了圖片左移和文字右移另玖。如果使用imageEdgeInsets和titleEdgeInsets困曙,對應(yīng)的也是這兩種處理方式。我們可以調(diào)整imageEdgeInsets來使圖片左移谦去,同理也可以調(diào)整titleEdgeInsets來使圖片右移慷丽,可以達(dá)到相同的效果。
寫代碼驗證一下鳄哭。首先看一下普通狀態(tài)的UIButton:
為了后續(xù)方便盈魁,先定義一些宏:
#define DefaultFont [UIFont systemFontOfSize:20.0f]
#define DefaultImage [UIImage imageNamed:@"mail"]
#define DefaultText @"技術(shù)支持"
生成一個普通的UIButton:
- (UIButton *)createBtn
{
UIButton *btn = [[UIButton alloc] init];
[btn setTitle:DefaultText forState:UIControlStateNormal];
btn.titleLabel.font = DefaultFont;
[btn setImage:DefaultImage forState:UIControlStateNormal];
[btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
btn.titleLabel.backgroundColor = [UIColor redColor];
return btn;
}
將普通button顯示到屏幕上:
UIButton *btn0 = [self createBtn];
[btn0 sizeToFit];
btn0.frame = CGRectMake(100,100,btn0.frame.size.width,btn0.frame.size.height);
[self.view addSubview:btn0];
看一下效果:
可以看到圖片和文字是緊緊貼在一起的。
調(diào)整imageEdgeInsets的值窃诉,使兩者保持一定間距杨耙。像上文說的,讓圖片左移飘痛,既然是左移珊膜,相當(dāng)于是左側(cè)擴(kuò)張,為了不拉伸圖片,對應(yīng)的右側(cè)就要縮減。也就是說左側(cè)的值為負(fù)描沟,右側(cè)的值為正饭寺,且兩者的絕對值要相等崔兴。
UIButton *btn1 = [self createBtn];
btn1.imageEdgeInsets = UIEdgeInsetsMake(0, -5, 0, 5);
[btn1 sizeToFit];
btn1.frame = CGRectMake(100,200,btn1.frame.size.width,btn1.frame.size.height);
[self.view addSubview:btn1];
看一下效果:
[圖片上傳失敗...(image-af7662-1545731919275)]
可以看到圖片和文字之間有了一定的間距厚脉。
接下來再來看一下蜕猫,使用imageEdgeInsets和titleEdgeInsets箫踩,讓文字在左塑陵,圖片在右感憾,為達(dá)到這種效果,imageEdgeInsets和titleEdgeInsets的值都需要調(diào)整令花。先看下圖:
[圖片上傳失敗...(image-7c71bb-1545731919275)]
默認(rèn)圖片在左阻桅,文字在右,現(xiàn)在要將文字放到左側(cè)兼都,圖片放到右側(cè)嫂沉。從上圖可以看出,最終文字是向左移了扮碧,圖片是向右移了趟章。根據(jù)前面的介紹,文字向左側(cè)移動慎王,那么titleEdgeInsets中的left字段應(yīng)該為負(fù)尤揣,right字段應(yīng)該為正,那么應(yīng)該移動多少呢柬祠?從上面的圖也很清晰的看到北戏,移動的距離就是圖片的寬度。再看圖片漫蛔,圖片整體右移嗜愈,因此imageEdgeInsets中的left字段應(yīng)該為正,right字段應(yīng)該為負(fù)莽龟,右移的距離是多少呢蠕嫁?正好是文字的寬度。
看一下實現(xiàn)代碼:
UIButton *btn2 = [self createBtn];
UILabel *label = [[UILabel alloc] init];
label.font = DefaultFont;
label.text = DefaultText;
[label sizeToFit];
btn2.imageEdgeInsets = UIEdgeInsetsMake(0, label.frame.size.width, 0, label.frame.size.width * -1);
btn2.titleEdgeInsets = UIEdgeInsetsMake(0, DefaultImage.size.width * -1, 0, DefaultImage.size.width);
[btn2 sizeToFit];
btn2.frame = CGRectMake(100,300,btn2.frame.size.width,btn2.frame.size.height);
[self.view addSubview:btn2];
效果如下:
如果想讓兩者之間有間距毯盈,也很簡單剃毒,移動時只需要把值調(diào)大一些即可:
UIButton *btn3 = [self createBtn];
btn3.imageEdgeInsets = UIEdgeInsetsMake(0, label.frame.size.width, 0, label.frame.size.width * -1);
btn3.titleEdgeInsets = UIEdgeInsetsMake(0, DefaultImage.size.width * -1 - 5, 0, DefaultImage.size.width + 5);
[btn3 sizeToFit];
btn3.frame = CGRectMake(100,400,btn3.frame.size.width,btn3.frame.size.height);
[self.view addSubview:btn3];
讓兩者之間保持間距為5,效果如下:
再來看最后一種效果搂赋,即圖片在上赘阀,文字在下。為達(dá)到這樣的效果脑奠,我們可以將圖片向上移基公,文字的y值保持不變;也可以圖片保持不變宋欺,文字的位置下移轰豆;可以達(dá)到相同的效果胰伍。如下圖:
當(dāng)然,也可以圖片上移一部分酸休,文字下移一部分骂租,只是沒有這個必要。
我們以圖片上移為例斑司。圖片上移渗饮,說明imageView的edgeInsets的top值擴(kuò)張,即top為負(fù)陡厘,那么bottom值為正抽米。移動的距離正好是圖片的高度特占。
btn4.imageEdgeInsets = UIEdgeInsetsMake(-DefaultImage.size.height, 0, DefaultImage.size.height, 0);
為了保持美觀糙置,我們還需要讓titleLabel和imageView的中心點對齊,即titleLabel左移。titleLabel左移是目,說明titleLabel的edgeInsets的left擴(kuò)張谤饭,值為負(fù),right值為正懊纳。titleLabel左移的距離需要計算揉抵。從上圖可以看出,titleLabel原來的x值正好是imageView.width嗤疯,現(xiàn)在的x值是(imageView.width - titleLabel.width) * 0.5,因此冤今,titleLabel需要左移的距離是:
CGFloat diff5X = DefaultImage.size.width - (DefaultImage.size.width - label.frame.size.width) * 0.5;
通常情況下,titleLabel的高度和imageView的高度是不同的茂缚,如下圖:
[圖片上傳失敗...(image-6c05b6-1545731919276)]
titleLabel和imageView豎直方向的中心點一致戏罢,但是兩者的高度是不同的。如果titleLabel的y值不調(diào)整脚囊,那么圖片和文字之間會有一定的間距龟糕,這個間距對于開發(fā)者來說不好控制。因此悔耘,最好的辦法是將titleLabel向上調(diào)整讲岁,和imageView緊鄰。調(diào)整的距離是多少呢衬以?根據(jù)上圖也很容易看出來
CGFloat diffY = (DefaultImage.size.height - label.frame.size.height) * 0.5;
完整代碼如下:
UIButton *btn4 = [self createBtn];
btn4.imageEdgeInsets = UIEdgeInsetsMake(-DefaultImage.size.height, 0, DefaultImage.size.height, 0);
CGFloat diff = DefaultImage.size.width - (DefaultImage.size.width - label.frame.size.width) * 0.5;
CGFloat diffY = (DefaultImage.size.height - label.frame.size.height) * 0.5;
btn4.titleEdgeInsets = UIEdgeInsetsMake(-diffY, diff * -1, diffY, diff);
[btn4 sizeToFit];
btn4.frame = CGRectMake(20,550,btn4.frame.size.width,btn4.frame.size.height);
[self.view addSubview:btn4];
效果圖如下:
如果想讓文字和圖片之間保持間距缓艳,只需要把文字向下移動一些即可,代碼如下:
UIButton *btn5 = [self createBtn];
btn5.imageEdgeInsets = UIEdgeInsetsMake(-DefaultImage.size.height, 0, DefaultImage.size.height, 0);
CGFloat diff5X = DefaultImage.size.width - (DefaultImage.size.width - label.frame.size.width) * 0.5;
CGFloat diff5Y = (DefaultImage.size.height - label.frame.size.height) * 0.5;
btn5.titleEdgeInsets = UIEdgeInsetsMake(-diffY + 5, diff * -1, diffY - 5, diff);
[btn5 sizeToFit];
btn5.frame = CGRectMake(200,550,btn5.frame.size.width,btn5.frame.size.height);
[self.view addSubview:btn5];
效果圖如下:
封裝
了解了imageEdgeInsets和titleEdgeInsets如何使用后看峻,之后再碰到文中開頭提到打需求郎任,相信大家已經(jīng)可以從容應(yīng)對了。然而备籽,imageEdgeInsets和titleEdgeInsets實在難以理解舶治,一段時間不用分井,可能就忘了應(yīng)該如何調(diào)整偏移量……每次使用之前都研究一下其偏移量的原理,好像又有點浪費時間霉猛。
為了方便日常開發(fā)尺锚,我封裝了ACEdgeInsetsBtn,ACEdgeInsetsBtn是UIButton的子類惜浅,主要目的就是解決UIButton中文字瘫辩、圖片有間隔、或者位置調(diào)整的問題坛悉。使用也很簡單伐厌,只需要傳入image,文案,字體裸影,imageView和titleLabel的間距挣轨,類型即可,如下:
/**
初始化方法
@param image btn所要顯示的image
@param text btn所要顯示的text
@param font btn的字體信息
@param edgeInsetsType 圖片的位置類型
@param space image和文字之間的間距
@return 返回實例button對象
*/
- (instancetype)initWithImage:(UIImage *)image text:(NSString *)text font:(UIFont *)font edgeInsetsType:(ACEdgeInsetsBtnType)edgeInsetsType space:(CGFloat)space;
ACEdgeInsetsBtnType是一個枚舉類型轩猩,目前包含了圖片左文子右卷扮、圖片右文字左、圖片上文字下三種類型均践,如下:
/**
btn類型晤锹,以圖片的位置為標(biāo)準(zhǔn)
*/
typedef NS_ENUM(NSInteger, ACEdgeInsetsBtnType){
// 普通狀態(tài),即圖片在左彤委,文字在右
ACEdgeInsetsBtnTypeNormal = 1,
// 圖片在右鞭铆,文字在左
ACEdgeInsetsBtnTypeRight ,
// 圖片在上,文字在下
ACEdgeInsetsBtnTop
};
傳入對應(yīng)的參數(shù)焦影,直接生成期望的Btn:
ACEdgeInsetsBtn *btn = [[ACEdgeInsetsBtn alloc] initWithImage:DefaultImage text:DefaultText font:DefaultFont edgeInsetsType:ACEdgeInsetsBtnTypeNormal space:5];
[btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
btn.frame = CGRectMake(50,80,btn.frame.size.width,btn.frame.size.height);
[self.view addSubview:btn];
項目放在了github上车遂,地址。