[iOS]調整UIButton的title和image詳解

關于UIButton大家都很熟悉,系統(tǒng)默認的樣式,是image在左,title在右的,如下圖所示:

系統(tǒng)默認樣式

但是,很多情況下UI的設計可不是這么樣的,最常用的是image在右邊,title在左的button,和類似分享頁面的那種上面是image,下面是title的button;如下圖所示:

標題在左,image在右
標題在下,image在上

筆者每次需要這種需求的時候,都是查很多資料,看了一些不痛不癢的介紹,然后再很復雜的實現(xiàn);效果暫且不說,耽誤了大量的時間,所以,花點時間研究了一下這種效果實現(xiàn),在這里總結一下:

PS:如果你只是想使用這種效果,可直接下載Demo1,Demo2,里面有筆者擴展的方法可以直接使用;如果你想了解其中的原理,請耐心看完本文.本文是對筆者實現(xiàn)這種效果以及之間遇到的問題的一個總結;

這里,筆者介紹兩種實現(xiàn)的方式,一種是通過對系統(tǒng)的UIButton進行擴展(category),一種是通過繼承自UIButton,自定義;

1.對UIButton擴展(category)

這種方式主要是用到了UIButton的下面兩個屬性:

@property(nonatomic) UIEdgeInsets titleEdgeInsets; // default is UIEdgeInsetsZero  
@property(nonatomic) UIEdgeInsets imageEdgeInsets; // default is UIEdgeInsetsZero  

下面以設置為title在左,image在右為例進行介紹:
首先來看一下這兩個屬性的類型:

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;  

他有四個參數(shù): top left bottom right表示上左下右的偏移量,其含義為:

top : 為正數(shù)的時候,是往下偏移,為負數(shù)的時候往上偏移;
left : 為正數(shù)的時候往右偏移,為負數(shù)的時候往左偏移;
bottom : 為正數(shù)的時候往上偏移,為負數(shù)的時候往下偏移;
right :為正數(shù)的時候往左偏移,為負數(shù)的時候往右偏移;

回過頭來看這兩個屬性titleEdgeInsets就是設置title的偏移量,imageEdgeInsets就是設置image的偏移量;
那么問題來了,怎樣設置這個偏移量呢?很多介紹這種方式的使用的偏移量的都是固定的值(但是這些值是從哪里參考而來并沒有說明),一旦你的button的frame和他介紹的不一樣,就需要多次修改,以找到最合適的偏移量,看得云里霧里不說,調整偏移量也花費了很多時間;經過筆者的一些測試,發(fā)現(xiàn)還是有些公共的東西可以使用的:
相信大家第一個想到的就是UIButtontitleLabelimageViewframe;所以,首先獲取他們的frame:

CGRect titleFrame = self.titleLabel.frame;
CGRect imageFrame = self.imageView.frame;

// title和image之間的間隙
CGFloat space = titleFrame.origin.x - imageFrame.origin.x - imageFrame.size.width;

然后設置他們各自偏移量:

[self setImageEdgeInsets:UIEdgeInsetsMake(0,titleFrame.size.width + space, 0, -(titleFrame.size.width + space))];
 [self setTitleEdgeInsets:UIEdgeInsetsMake(0, -(titleFrame.origin.x - imageFrame.origin.x), 0, titleFrame.origin.x - imageFrame.origin.x)];

這兩個屬性的默認值都是0,所以,在不需要偏移的方向上,偏移量設置為0即可,那么,怎么知道哪個方向需要偏移,哪個方向不需要偏移呢?
其實,很簡單,只需要仔細觀察,偏移前(系統(tǒng)默認布局)和偏移后(你想要的布局)有哪些變化:

對于image:由左邊移動到右邊,可知,上下不變,左右偏移,即image的left和right變化;
對于title:由右邊移動到左邊,同樣是上下不變,左右偏移,即title的left和right變化;

清楚了哪些方向上有變化,接下來就是變化多少的問題了:
因為要把image移動到button的右邊,需要往右移動,所以imageEdgeInsets距左邊界(left)的偏移量需要設置為標題的寬度加上他們之間的間隙,即:titleSize.width + space,右邊的偏移量(right)同樣是titleSize.width + space,但是應該是負的,即:-(titleFrame.size.width + space);其他方向沒有移動,直接設為默認值0;
同理,title需要往左移動,需要設置titleEdgeInsets距離左邊界(left)的偏移量為負的image + space的寬度,即: '-(imageSize.width + space' (同 '-(titleFrame.origin.x - imageFrame.origin.x)' ),此時title距離右邊界的偏移量(right)就不是0了,而應該是image + space的寬度,即:imageSize.width + space;
設置完后,看一下效果,似乎并不是預想的那樣:

title在左,image在右

回頭仔細看了下設置,邏輯上似乎并沒有錯誤,那是哪兒出了問題呢?仔細查找后找到了問題,因為這時獲取到的title的size值為0:

title的size

>對于這個問題的原因,筆者查了一些資料,暫時還沒有找到相關的介紹,也沒有搞清楚到底是什么原因導致的,如果你知道原因,還請留言告知,謝謝

但是,筆者偶然間發(fā)現(xiàn),只要在獲取titleSize之前,使用一次buttontitleLabelimageView,就能獲取到他的size了,設置一下titleLabelimageView的任意屬性都行,如果不需要設置這些屬性,可以和我一樣,設置一下它的背景色和button一致(只是為了提前使用一次):

button.titleLabel.backgroundColor = button.backgroundColor;  
button.imageView.backgroundColor = button.backgroundColor;  

PS:這樣雖然能夠獲取到titleSize,但是多這么一個操作,實在不是正常的,如果你有更好的方式獲取,還請留言告知,感謝!!

PS: 新增

關于獲取的titleLabel的frame為zero可以參考這里的介紹, button的title被設置后,并不會立馬去更新其frame, 所以需要在合適的地方主動更新, 這里使用的是解決方法中的第一種, 即:

[self layoutIfNeeded];

這樣設置之后,基本能夠實現(xiàn)需求了:

[self setImageEdgeInsets:UIEdgeInsetsMake(0,titleFrame.size.width + space, 0, -(titleFrame.size.width + space))];
[self setTitleEdgeInsets:UIEdgeInsetsMake(0, -(titleFrame.origin.x - imageFrame.origin.x), 0, titleFrame.origin.x - imageFrame.origin.x)];

效果圖如下:

和系統(tǒng)UIButton默認情況的對比

會發(fā)現(xiàn),幾乎和系統(tǒng)默認的一致;
最終實現(xiàn)效果的完整設置(主要是獲取size的時機)為:

[self layoutIfNeeded];
CGRect titleFrame = self.titleLabel.frame;
CGRect imageFrame = self.imageView.frame;
CGFloat space = titleFrame.origin.x - imageFrame.origin.x - imageFrame.size.width
      
[self setImageEdgeInsets:UIEdgeInsetsMake(0,titleFrame.size.width + space, 0, -(titleFrame.size.width + space))];
[self setTitleEdgeInsets:UIEdgeInsetsMake(0, -(titleFrame.origin.x - imageFrame.origin.x), 0, titleFrame.origin.x - imageFrame.origin.x)]; 

最終效果圖:


最終效果圖
  • 修改為圖片再上,標題在下樣式
    過程基本相同,只是在最后設置的時候不同,這里直接給出主要設置代碼:
[self layoutIfNeeded];
CGRect titleFrame = self.titleLabel.frame;
CGRect imageFrame = self.imageView.frame;
CGFloat space = titleFrame.origin.x - imageFrame.origin.x - imageFrame.size.width    
[self setImageEdgeInsets:UIEdgeInsetsMake(0,0, titleFrame.size.height + space, -(titleFrame.size.width))];
[self setTitleEdgeInsets:UIEdgeInsetsMake(imageFrame.size.height + space, -(imageFrame.size.width), 0, 0)];  

效果圖:

標題在下

針對此方法,本人寫了一個UIButton的category:Demo地址,里面的類目可直接拿來使用,簡單調用一個方法即可;

2.通過自定義Button

該方法,是通過自定義一個button,繼承自UIButton,然后重寫下面兩個方法:

- (CGRect)titleRectForContentRect:(CGRect)contentRect;  
- (CGRect)imageRectForContentRect:(CGRect)contentRect;  

對于這兩個方法,網上的一些介紹真是不得不吐槽了,模糊不清;后來根據(jù)筆者多次嘗試,個人理解為:方法的參數(shù)contentRect,即內容的frame,其值和buttonbounds是一樣的,通過這個參數(shù)可以獲取到當前buttonsize;返回值為CGRect類型,即是titleimagebutton的絕對坐標值;換句話說,這里返回的是一個絕對坐標,即button的子控件在button上的絕對布局,這里可以返回一個寫死的frame(查到的使用此方法的也都是寫死的frame),但要注意,不要超過contentRect的范圍**;
有一點需要說明:這兩個方法不是只調用一次,會被多次調用,只要buttontitle改變,都會調用此方法,最后一次調用,返回的frame值,才是最終的布局frame,所以,在這里,可以通過獲取button的標題,動態(tài)地修改其frame,使titleimage顯示緊湊;
明白了這兩個方法,下面就開始使用它:

  • 標題在左側,圖像在右側
    一般這種布局的button都是寬度比高大很多,所以,這里我以button的高度為參考來設置imageViewtitleLabel的高度,即:
CGFloat inteval = CGRectGetHeight(contentRect)/8.0;  
//設置圖片的寬高為button高度的3/4;  
CGFloat imageH = CGRectGetHeight(contentRect) - 22 * inteval; 

這個間隔可以根據(jù)自己的需求修改;
然后設置imageViewframe:

CGRect rect = CGRectMake(CGRectGetWidth(contentRect) - imageH - inteval, inteval, imageH, imageH);  

即:

-(CGRect)imageRectForContentRect:(CGRect)contentRect {  
      CGFloat inteval = CGRectGetHeight(contentRect)/8.0;  
          
      //設置圖片的寬高為button高度的3/4;  
      CGFloat imageH = CGRectGetHeight(contentRect) - 22 * inteval;  
          
      CGRect rect = CGRectMake(CGRectGetWidth(contentRect) - imageH - inteval, inteval, imageH, imageH);  
          
      return rect;  
}  

下面來設置titleLabelframe:

-(CGRect)titleRectForContentRect:(CGRect)contentRect {  
      
    CGFloat inteval = CGRectGetHeight(contentRect)/8.0;  
    //設置圖片的寬高為button高度的3/4;  
    CGFloat imageH = CGRectGetHeight(contentRect) - 22 * inteval;  
          
    CGRect rect = CGRectMake(inteval, inteval, CGRectGetWidth(contentRect) - imageH - 2*inteval, CGRectGetHeight(contentRect) - 2*inteval);  
          
    return rect;  
}  

效果圖:

效果圖

這種設置的方法好處是,可以手動控制titleLabelimageViewframe,但是不足之處是,不能像系統(tǒng)的那樣根據(jù)圖片的scaletitle字符串的大小來設置frame的大小;
buttonframe能夠很好的包含titleimage的時候,效果基本差不多;當buttonframe不適合時區(qū)別還是很明顯的:

frame設置合理
frame設置不合理

可以看出,主要的區(qū)別是:系統(tǒng)默認的可以動態(tài)地修改子控件的frame,雖然顯示效果也不是很理想;

  • 標題在底部,圖像在上面
    思路基本一致,這里直接給出設置代碼:
-(CGRect)imageRectForContentRect:(CGRect)contentRect {  
     
     CGFloat inteval = CGRectGetWidth(contentRect)/16.0;  
     inteval = MIN(inteval, 6);  
          
     //設置圖片的寬高為button寬度的7/8;  
     CGFloat imageW = CGRectGetWidth(contentRect) - 22 * inteval;  
          
     CGRect rect = CGRectMake(inteval, inteval, imageW, imageW);  
          
     return rect;  
}  
-(CGRect)titleRectForContentRect:(CGRect)contentRect {  
     CGFloat inteval = CGRectGetWidth(contentRect)/16.0;  
     inteval = MIN(inteval, 6);  
          
     //設置圖片的寬高為button寬度的7/8;  
     CGFloat imageW = CGRectGetWidth(contentRect) - 22 * inteval;  
          
     CGRect rect = CGRectMake(0, inteval*2 + imageW, CGRectGetWidth(contentRect) , CGRectGetHeight(contentRect) - 3*inteval - imageW);  
          
     return rect;  
}  

效果圖:

效果圖

這里有一點說明:就是標題的對齊方式,上面的效果圖是設置了居中對齊,系統(tǒng)默認是居左的,什么時候更改這個設置呢?
我的做法是這樣的,因為我增加了一個自定義屬性,用來選擇設置的button的樣式:

#import <UIKit/UIKit.h>  
  
typedef NS_ENUM(NSInteger,LZRelayoutButtonType) {  
    LZRelayoutButtonTypeNomal  = 0,//默認  
    LZRelayoutButtonTypeLeft   = 1,//標題在左  
    LZRelayoutButtonTypeBottom = 2,//標題在下  
};  
  
@interface LZRelayoutButton : UIButton  
  
@property (assign,nonatomic)LZRelayoutButtonType lzType;  
@end  

我重寫了它的setter方法:

-(void)setLzType:(LZRelayoutButtonType)lzType {  
    _lzType = lzType;  
      
    if (lzType != LZRelayoutButtonTypeNomal) {  
        self.titleLabel.textAlignment = NSTextAlignmentCenter;  
    }  
}  

這種方式的設置,我寫了一個Demo,里面的自定義button可以拿來直接使用,如果對你有幫助,還請star支持一下,感謝!!!

總結

以上兩種方式都能實現(xiàn)重新布局UIButton的子控件的效果,各有優(yōu)缺點:

第一種方式:需要精確的設置偏移量,但是有些量是無法獲取的,只能在使用時調整,特別是設置標題在底部時,總感覺image的左右距離button的左右間隙不一致;

第二種方式:對frame的控制就比較自由了,需要注意的是對間隔的把控,使用這種方式就沒有第一種的方式問題了;

不管是系統(tǒng)默認,還是我們修改之后的button,其frame的設置都是很重要的,不合適的frame會讓button看起來很不舒服...

PS:因為文章是從本人CSDN博客遷移而來,所以,文中使用圖片帶有CSDN的水印,原文 地址: [iOS]詳解調整UIButton的title和image的位置

(完)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末冠息,一起剝皮案震驚了整個濱河市害驹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌武氓,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件岭埠,死亡現(xiàn)場離奇詭異脆丁,居然都是意外死亡,警方通過查閱死者的電腦和手機稚叹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門焰薄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人扒袖,你說我怎么就攤上這事塞茅。” “怎么了季率?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵野瘦,是天一觀的道長。 經常有香客問我蚀同,道長缅刽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任蠢络,我火速辦了婚禮衰猛,結果婚禮上,老公的妹妹穿的比我還像新娘刹孔。我一直安慰自己啡省,他們只是感情好娜睛,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著卦睹,像睡著了一般畦戒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上结序,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天障斋,我揣著相機與錄音,去河邊找鬼徐鹤。 笑死垃环,一個胖子當著我的面吹牛,可吹牛的內容都是我干的返敬。 我是一名探鬼主播遂庄,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼劲赠!你這毒婦竟也來了涛目?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤凛澎,失蹤者是張志新(化名)和其女友劉穎霹肝,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體预厌,經...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡阿迈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了轧叽。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡刊棕,死狀恐怖炭晒,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情甥角,我是刑警寧澤网严,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站嗤无,受9級特大地震影響震束,放射性物質發(fā)生泄漏。R本人自食惡果不足惜当犯,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一垢村、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧嚎卫,春花似錦嘉栓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽麻昼。三九已至,卻和暖如春馋辈,著一層夾襖步出監(jiān)牢的瞬間抚芦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工迈螟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留叉抡,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓井联,卻偏偏與公主長得像卜壕,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子烙常,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內容