iOS button 裡邊圖片和文字的距離

來源:互聯網
上載者:User

標籤:jsb   return   back   cat   property   imageview   寬度   class   net   

很多次,系統預設的UIButton不能滿足需求,每次都是查了很多資料,最後還是用最複雜的方式來修改button的標題和圖片的位置,今天,花些時間來梳理一下這方面的知識...

UIButton的預設布局是:title在右,image在左;

很多時候我們需要的是title在左邊,或者title在下面,這時就需要調整UIButton的TitleLabel和ImageView的位置了,查了很多資料,要麼零零散散的介紹,要麼就是特別複雜的實現;經過一段時間的學習,在這裡總結一下實現的方式;

一種是設定UIButton的以下兩個屬性:

 

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


還有一種就是,自訂button,繼承自UIButton,重寫UIButton的以下兩個方法:

 

 

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


下面來介紹這兩種實現方式:

 

一,設定屬性

     1.修改為標題在左,圖片在右樣式

其屬性的類型為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;


表示 上 左 下 右的位移量;解釋下,這四個量的含義:

 

top : 為正數的時候,是往下位移,為負數的時候往上位移;

left : 為正數的時候往右位移,為負數的時候往左位移;

bottom : 為正數的時候往上位移,為負數的時候往下位移;

right :為正數的時候往左位移,為負數的時候往右位移;

在設定UIButton的這兩個屬性時,遇到的第一個問題就是位移量設定為多少?很多介紹這種方法的使用的都是固定值,當按鈕的frame改變時,需要改動很多地方,看的有點雲裡霧裡的;

經過一些研究,發現還是有一些公用的東西可以使用的:

第一個想到的是UIButton的標題titleLabel的frame和imageView的frame,所以,先擷取titleLabel和imageView的size:

 

CGSize titleSize = button.titleLabel.bounds.size;CGSize imageSize = button.imageView.bounds.size;

然後設定UIButton的屬性:

 

 

button.imageEdgeInsets = UIEdgeInsetsMake(0,titleSize.width, 0, -titleSize.width);button.titleEdgeInsets = UIEdgeInsetsMake(0, -imageSize.width, 0, imageSize.width);


因為這兩個屬性的預設值都是0,所以,在不需要位移的方向上,位移量設定為0即可,那麼,怎麼知道哪個方向需要位移,哪個方向不需要位移呢?其實,很簡單,只需要仔細觀察,位移前(系統預設布局)和位移後(你想要的布局)有哪些變化;

 

對於image:由左邊移動到右邊,可知,上下不變,左右位移,即image的left和right變化;

對於title:由右邊移動到左邊,同樣是上下不變,左右位移,即title的left和right變化;

下面解釋一下需要改變的方向位移量設定的含義:

因為要把image移動到button的右邊,需要往右移動,所以imageEdgeInsets距左邊界的位移量設定為標題的寬度,即:titleSize.width,右邊的位移量同樣是titleSize.width,但是應該是負的,其他方向沒有移動,直接設為預設值0;

同理,title需要往左移動,需要設定titleEdgeInsets距離左邊界的位移量為負的image的寬度,即:-imageSize.width
這樣,同樣,此時title距離右邊界的位移量就不是0了,而應該是image的寬度,即:imageSize.width;

這樣就設定完了,但是結果好像並不是那麼理想:

仔細尋找後發現,擷取到的titleSize的為0:

查了一些資料,沒有找到相關的解釋,暫時也沒搞清楚是什麼原因,如果你知道,還請留言告知,感謝!

但是,偶然間發現,只要在擷取titleSize之前,使用一次button的titleLabel和imageView,就能擷取到他的size了,設定一下titleLabel和imageView的任意屬性都行,如果不需要設定這些屬性,可以和我一樣,設定一下它的背景色和button一致(只是為了提前使用一次):

 

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


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

 

這樣設定之後,基本能夠實現需求了,但是子控制項之間是有間隙的,這裡我設定了1像素的寬度:

 

CGFloat interval = 1.0;

 

然後設定button的兩個屬性:

 

button.imageEdgeInsets = UIEdgeInsetsMake(0,titleSize.width + interval, 0, -(titleSize.width + interval));button.titleEdgeInsets = UIEdgeInsetsMake(0, -(imageSize.width + interval), 0, imageSize.width + interval);


效果如下:

 

會發現,幾乎和系統預設的一致;

最終實現完整設定(主要是擷取size的時機)為:

 

button.titleLabel.backgroundColor = button.backgroundColor;button.imageView.backgroundColor = button.backgroundColor;//在使用一次titleLabel和imageView後才能正確擷取titleSize    CGSize titleSize = button.titleLabel.bounds.size;CGSize imageSize = button.imageView.bounds.size;CGFloat interval = 1.0;    button.imageEdgeInsets = UIEdgeInsetsMake(0,titleSize.width + interval, 0, -(titleSize.width + interval));button.titleEdgeInsets = UIEdgeInsetsMake(0, -(imageSize.width + interval), 0, imageSize.width + interval);

最終:

 



2. 修改為,圖片在上,標題在下樣式

過程基本相同,只是在最後設定的時候不同,這裡直接給出主要設定代碼:

 

button.titleLabel.backgroundColor = button.backgroundColor;button.imageView.backgroundColor = button.backgroundColor;CGSize titleSize = button.titleLabel.bounds.size;CGSize imageSize = button.imageView.bounds.size;CGFloat interval = 1.0;    [button setImageEdgeInsets:UIEdgeInsetsMake(0,0, titleSize.height + interval, -(titleSize.width + interval))];[button setTitleEdgeInsets:UIEdgeInsetsMake(imageSize.height + interval, -(imageSize.width + interval), 0, 0)];


:

 

在設定位移量的時候,誤差還是有的,經過測試,最好的解決方式是,button的大小設定,要恰到好處能夠容下標題和圖片,對於追求完美的人,可自己修改位移參數;

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

二,通過自訂Button

該方法,是通過自訂一個button,繼承自UIButton,然後重寫方法:

 

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


對於這兩個方法,網上的一些介紹真是不得不吐槽了,模糊不清;後來根據個人嘗試,個人理解為:方法的參數contentRect,即內容的frame,其值和button的bounds是一樣的,通過這個參數可以擷取到當前button的size;傳回值為CGRect類型,即是title或image在button的絕對座標值;換句話說,這裡返回的是一個絕對座標,即button的子控制項在button上的絕對布局,這裡可以返回一個寫死的frame(查到的使用此方法的也都是寫死的frame),但要注意,不要超過contentRect的範圍;

 

有一點需要說明:這兩個方法不是只調用一次,會被多次調用,只要button的title改變,都會調用此方法,最後一次調用,返回的frame值,才是最終的布局frame,所以,在這裡,可以通過擷取button的標題,動態地修改其frame,使title和image顯示緊湊;

明白了這兩個方法,下面就開始使用它:

1.標題在左側,映像在右側

一般這種布局的不同都是寬度比高大很多,所以,這裡我以button的高度為參考來設定imageView和titleLabel的高度,即:

 

CGFloat inteval = CGRectGetHeight(contentRect)/8.0;        //設定圖片的寬高為button高度的3/4;CGFloat imageH = CGRectGetHeight(contentRect) - 2 * inteval;


這個間隔可以根據自己的需求修改;

 

然後設定imageView的frame:

 

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) - 2 * inteval;              CGRect rect = CGRectMake(CGRectGetWidth(contentRect) - imageH - inteval, inteval, imageH, imageH);              return rect;}


下面來設定titleLabel的frame:

 

 

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

:

 


這種設定的方法好處是,可以手動控制titleLabel和imageView的frame,但是不足之處是,不能像系統的那樣根據圖片的scale和title字串的大小來設定frame的大小;

當button的frame能夠很好的包含title和image的時候,效果基本差不多;當button的frame不適合時區別還是很明顯的:



可以看出,主要的區別是:系統預設的可以動態地修改子控制項的frame,雖然顯示效果也不是很理想;

2.標題在底部,映像在上面

思路基本一致,這裡直接給出設定代碼:

 

- (CGRect)imageRectForContentRect:(CGRect)contentRect {        CGFloat inteval = CGRectGetWidth(contentRect)/16.0;     inteval = MIN(inteval, 6);             //設定圖片的寬高為button寬度的7/8;     CGFloat imageW = CGRectGetWidth(contentRect) - 2 * 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) - 2 * inteval;             CGRect rect = CGRectMake(0, inteval*2 + imageW, CGRectGetWidth(contentRect) , CGRectGetHeight(contentRect) - 3*inteval - imageW);             return rect;}


:

 

這裡有一點說明:就是標題的對齊,上面的是設定了置中對齊,系統預設是居左的,什麼時候更改這個設定呢?

我的做法是這樣的,因為我增加了一個自訂屬性:

 

#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:github地址,裡面的自訂button可以拿來直接使用,如果對你有協助,還請star支援一下,感謝!!!

 

總結:以上兩種方式都能實現重新布局UIButton的子控制項的效果,各有優缺點:

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

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

不管是系統預設,還是我們修改之後的,button的frame設定都是很重要的,不合適的frame會讓button看起來很不舒服...

iOS button 裡邊圖片和文字的距離

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.