UIButton的預設佈局是:title在右,image在左; 很多時候我們需要的是title在左邊,或者title在下麵,這時就需要調整UIButton的TitleLabel和ImageView的位置了,查了很多資料,要麼零零散散的介紹,要麼就是特別複雜的實現;經過一段時間的學習,在這裡總結一下實 ...
UIButton的預設佈局是:title在右,image在左;
很多時候我們需要的是title在左邊,或者title在下麵,這時就需要調整UIButton的TitleLabel和ImageView的位置了,查了很多資料,要麼零零散散的介紹,要麼就是特別複雜的實現;經過一段時間的學習,在這裡總結一下實現的方式;
一種是設置UIButton的以下兩個屬性:
[objc] view plain copy
- @property(nonatomic) UIEdgeInsets titleEdgeInsets; // default is UIEdgeInsetsZero
- @property(nonatomic) UIEdgeInsets imageEdgeInsets; // default is UIEdgeInsetsZero
還有一種就是,自定義button,繼承自UIButton,重寫UIButton的以下兩個方法:
[objc] view plain copy
- - (CGRect)titleRectForContentRect:(CGRect)contentRect;
- - (CGRect)imageRectForContentRect:(CGRect)contentRect;
下麵來介紹這兩種實現方式:
一,設置屬性
1.修改為標題在左,圖片在右樣式
其屬性的類型為UIEdgeInsets,為一個結構體:
[objc] view plain copy
- 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:
[objc] view plain copy
- CGSize titleSize = button.titleLabel.bounds.size;
- CGSize imageSize = button.imageView.bounds.size;
然後設置UIButton的屬性:
[objc] view plain copy
- 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一致(只是為了提前使用一次):
[objc] view plain copy
- button.titleLabel.backgroundColor = button.backgroundColor;
- button.imageView.backgroundColor = button.backgroundColor;
PS:這樣雖然能夠獲取到titleSize,但是多這麼一個操作,實在不是正常的,如果你有更好的方式獲取,還請留言告知,感謝!!
這樣設置之後,基本能夠實現需求了,但是子控制項之間是有間隙的,這裡我設置了1像素的寬度:
[objc] view plain copy
- CGFloat interval = 1.0;
然後設置button的兩個屬性:
[objc] view plain copy
- button.imageEdgeInsets = UIEdgeInsetsMake(0,titleSize.width + interval, 0, -(titleSize.width + interval));
- button.titleEdgeInsets = UIEdgeInsetsMake(0, -(imageSize.width + interval), 0, imageSize.width + interval);
效果如下:
會發現,幾乎和系統預設的一致;
最終實現完整設置(主要是獲取size的時機)為:
[objc] view plain copy
- 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. 修改為,圖片在上,標題在下樣式
過程基本相同,只是在最後設置的時候不同,這裡直接給出主要設置代碼:
[objc] view plain copy
- 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,然後重寫方法:
[objc] view plain copy
- - (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的高度,即:
[objc] view plain copy
- CGFloat inteval = CGRectGetHeight(contentRect)/8.0;
- //設置圖片的寬高為button高度的3/4;
- CGFloat imageH = CGRectGetHeight(contentRect) - 22 * inteval;
這個間隔可以根據自己的需求修改;
然後設置imageView的frame:
[objc] view plain copy
- CGRect rect = CGRectMake(CGRectGetWidth(contentRect) - imageH - inteval, inteval, imageH, imageH);
即:
[objc] view plain copy
- - (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;
- }
下麵來設置titleLabel的frame:
[objc] view plain copy
- - (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;
- }
效果圖:
這種設置的方法好處是,可以手動控制titleLabel和imageView的frame,但是不足之處是,不能像系統的那樣根據圖片的scale和title字元串的大小來設置frame的大小;
當button的frame能夠很好的包含title和image的時候,效果基本差不多;當button的frame不適合時區別還是很明顯的:
可以看出,主要的區別是:系統預設的可以動態地修改子控制項的frame,雖然顯示效果也不是很理想;
2.標題在底部,圖像在上面
思路基本一致,這裡直接給出設置代碼:
[objc] view plain copy
- - (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;
- }
[objc] view plain copy
- - (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;
- }
效果圖:
這裡有一點說明:就是標題的對齊方式,上面的效果圖是設置了居中對齊,系統預設是居左的,什麼時候更改這個設置呢?
我的做法是這樣的,因為我增加了一個自定義屬性:
[objc] view plain copy
- #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方法:
[objc] view plain copy
- - (void)setLzType:(LZRelayoutButtonType)lzType {
- _lzType = lzType;
- if (lzType != LZRelayoutButtonTypeNomal) {
- self.titleLabel.textAlignment = NSTextAlignmentCenter;
- }
- }