imageEdgeInsets && titleEdgeInsets

原文連接git

前言

日常開發中,UIButton是使用頻率很是高的控件。除了可點擊以外,還由於其可以同時顯示文案和圖片。默認的UIButton,圖片在左,文字在右,且文字是牢牢挨着圖片的。大多數狀況下,UIButton已經能夠知足需求了,然而,總有一些意外……好比,設計讓文字和圖片不要離得很近;好比,設計讓文字在左,圖片在右;好比,設計讓圖片在上,文字在下……github

這時候就會用到本文的主角:imageEdgeInsets和titleEdgeInsets了。使用imageEdgeInsets和titleEdgeInsets時,可能或多或少的都會有一種感受,就是難以捉摸,每次都是嘗試屢次才能達到一個良好的效果。再完全會使用imageEdgeInsets和titleEdgeInsets以前,咱們先來分析下面對的問題。bash

圖片和文字保持間距

首先面臨的第一個問題就是圖片和文字之間保持一個距離。其實碰到這樣的需求解決方法有不少,若是對imageEdgeInsets和titleEdgeInsets不熟悉,徹底不必使用這兩個屬性。性能

圖片右側透明

UIButton 默認圖片在左,文字在右,且文字和圖片緊鄰,如今的需求是圖片和文字之間留有必定的間距。一種方法是,若是設計提供的圖片右側有一些透明的地方,那麼總體給用戶的感受就是圖片和文字之間有必定的間距。只不過這種方法須要讓設計切圖。字體

文案加空格

其實若是隻是想讓圖片和文字之間有必定間距,不用設計切圖,代碼徹底能夠控制,並且特別簡單。方法就是在文案的前面加空格,好比說原本要顯示的文案是"聯繫咱們",程序中能夠設置爲"      聯繫咱們",這樣最終展現給用戶的效果圖片和文件之間也有必定的間距。這種方法徹底是開發人員可控的。ui

圖片和文字位置調整

圖片和文字的位置調整,主要是指圖片在右,文字在左,或者圖片在上,文字在下這兩種狀況,目前尚未碰到過文字在上,圖片在下的需求。若是設計的圖是圖片和文字的位置有了調整,那麼僅僅靠增長圖片透明度、文案加空格等相似的方法是知足不了需求的。atom

解決方法有兩種,一種是使用UIView,另外就是使用imageEdgeInsets和titleEdgeInsets。spa

使用UIView解決

使用UIView的思路很是簡單。就是使用三個控件,一個UIView做爲父控件,一個UILabel用來顯示內容,一個UIImageView用來顯示圖片。因爲UILabel和UIImageView都是咱們本身寫的,其frame咱們能夠隨意控制,圖片是在左、在右,仍是在上,都是能夠控制的。設計

至於UIButton的點擊事件,由於UIView不具有點擊事件,咱們能夠給UIView增長手勢UITapGestureRecognizer,用來模擬點擊行爲。看上去完美解決了這個問題,可是這種方案是有缺點的,看一下:3d

  1. 首先這種方法是使用了3個控件,而若是使用UIButton,只須要使用1個控件。雖然UIButton內部也包含了一個UIImageView和一個UILabel,性能上可能沒有太大的差距,可是這種寫法麻煩啊。原本使用UIButton5行代碼就能夠搞定了,結果使用UIView的形式,寫了20行代碼,實現比較繁瑣。
  2. 雖然能夠給UIView添加手勢模擬點擊行爲,可是,UIView是沒有高亮狀態的。UIButton默認是有普通狀態和高亮狀態,點擊時會顯示默認的高亮狀態,這個效果UIView是實現不了的。若是想實現,須要再增長更多的代碼。

能夠說,使用UIView可以解決問題,可是解決方法不夠優雅。

imageEdgeInsets和titleEdgeInsets

蘋果可能已經考慮到了開發者會有這樣的需求,因而提供了imageEdgeInsets和titleEdgeInsets兩個屬性。然而,因爲蘋果沒有介紹這兩個屬性的原理,對兩個屬性的描述又不是特別清晰,致使使用起來難度較大,對於經驗不足的開發者,每次使用都要嘗試屢次。

先來看下二者的定義。

imageEdgeInsets和titleEdgeInsets的定義

imageEdgeInsets和titleEdgeInsets的定義在UIButton.h中,以下:

@property(nonatomic)          UIEdgeInsets titleEdgeInsets;                // default is UIEdgeInsetsZero
@property(nonatomic)          UIEdgeInsets imageEdgeInsets;                // default is UIEdgeInsetsZero
複製代碼

能夠看到,imageEdgeInsets和titleEdgeInsets都是UIEdgeInsets類型,且默認取值是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是一個結構體,四個值分別是上、左、下、右,值能夠爲正,也能夠爲負。

在瞭解imageEdgeInsets和titleEdgeInsets的使用以前,有三點必定要明白:

  1. 不管是imageEdgeInsets仍是titleEdgeInsets,都是和原控件的位置相比較的。imageEdgeInsets是和原imageView的位置比較,titleEdgeInsets是和原label的位置比較
  2. imageEdgeInsets和titleEdgeInsets中的值爲正,則是該方向上的擴張,若是值爲負,則是該方向上的縮減。舉例來講,對於左側,擴張是更向左,即frame的x值減少;對於右側擴張是更向右,frame的width值更大
  3. UIEdgeInsets是對稱的,左右對稱,上下對稱。所以,在設置imageEdgeInsets和titleEdgeInsets時儘可能也要對稱,好比[0,-5,0,5],左右對稱,同理上下也要對稱。之因此要對稱,是爲了避免拉伸imageView和titleLabel
imageEdgeInsets和titleEdgeInsets的使用

有了上面的瞭解和介紹,來看一下使用imageEdgeInsets和titleEdgeInsets如何解決文中最開始提到的問題。

首先是圖片和文字之間保持間距。圖片和文字保持間距有兩種處理方式,一種是圖片左移,一種是文字右移。實際上,最開始提到的給圖片右側增長透明度以及文案加空格正是分別對應了圖片左移和文字右移。若是使用imageEdgeInsets和titleEdgeInsets,對應的也是這兩種處理方式。咱們能夠調整imageEdgeInsets來使圖片左移,同理也能夠調整titleEdgeInsets來使圖片右移,能夠達到相同的效果。

寫代碼驗證一下。首先看一下普通狀態的UIButton:

爲了後續方便,先定義一些宏:

#define DefaultFont [UIFont systemFontOfSize:20.0f]
#define DefaultImage [UIImage imageNamed:@"mail"]
#define DefaultText @"技術支持"
複製代碼

生成一個普通的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];
複製代碼

看一下效果:

image

能夠看到圖片和文字是牢牢貼在一塊兒的。

調整imageEdgeInsets的值,使二者保持必定間距。像上文說的,讓圖片左移,既然是左移,至關因而左側擴張,爲了避免拉伸圖片,對應的右側就要縮減。也就是說左側的值爲負,右側的值爲正,且二者的絕對值要相等。

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

能夠看到圖片和文字之間有了必定的間距。

接下來再來看一下,使用imageEdgeInsets和titleEdgeInsets,讓文字在左,圖片在右,爲達到這種效果,imageEdgeInsets和titleEdgeInsets的值都須要調整。先看下圖:

image

默認圖片在左,文字在右,如今要將文字放到左側,圖片放到右側。從上圖能夠看出,最終文字是向左移了,圖片是向右移了。根據前面的介紹,文字向左側移動,那麼titleEdgeInsets中的left字段應該爲負,right字段應該爲正,那麼應該移動多少呢?從上面的圖也很清晰的看到,移動的距離就是圖片的寬度。再看圖片,圖片總體右移,所以imageEdgeInsets中的left字段應該爲正,right字段應該爲負,右移的距離是多少呢?正好是文字的寬度。

看一下實現代碼:

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];
複製代碼

效果以下:

image

若是想讓二者之間有間距,也很簡單,移動時只須要把值調大一些便可:

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,效果以下:

image

再來看最後一種效果,即圖片在上,文字在下。爲達到這樣的效果,咱們能夠將圖片向上移,文字的y值保持不變;也能夠圖片保持不變,文字的位置下移;能夠達到相同的效果。以下圖:

image

固然,也能夠圖片上移一部分,文字下移一部分,只是沒有這個必要。

咱們以圖片上移爲例。圖片上移,說明imageView的edgeInsets的top值擴張,即top爲負,那麼bottom值爲正。移動的距離正好是圖片的高度。

btn4.imageEdgeInsets = UIEdgeInsetsMake(-DefaultImage.size.height, 0, DefaultImage.size.height, 0);
複製代碼

爲了保持美觀,咱們還須要讓titleLabel和imageView的中心點對齊,即titleLabel左移。titleLabel左移,說明titleLabel的edgeInsets的left擴張,值爲負,right值爲正。titleLabel左移的距離須要計算。從上圖能夠看出,titleLabel原來的x值正好是imageView.width,如今的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

titleLabel和imageView豎直方向的中心點一致,可是二者的高度是不一樣的。若是titleLabel的y值不調整,那麼圖片和文字之間會有必定的間距,這個間距對於開發者來講很差控制。所以,最好的辦法是將titleLabel向上調整,和imageView緊鄰。調整的距離是多少呢?根據上圖也很容易看出來

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];
複製代碼

效果圖以下:

image

若是想讓文字和圖片之間保持間距,只須要把文字向下移動一些便可,代碼以下:

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];
複製代碼

效果圖以下:

image

封裝

瞭解了imageEdgeInsets和titleEdgeInsets如何使用後,以後再碰到文中開頭提到打需求,相信你們已經能夠從容應對了。然而,imageEdgeInsets和titleEdgeInsets實在難以理解,一段時間不用,可能就忘了應該如何調整偏移量……每次使用以前都研究一下其偏移量的原理,好像又有點浪費時間。

爲了方便平常開發,我封裝了ACEdgeInsetsBtn,ACEdgeInsetsBtn是UIButton的子類,主要目的就是解決UIButton中文字、圖片有間隔、或者位置調整的問題。使用也很簡單,只須要傳入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類型,以圖片的位置爲標準
 */
typedef NS_ENUM(NSInteger, ACEdgeInsetsBtnType){
    // 普通狀態,即圖片在左,文字在右
    ACEdgeInsetsBtnTypeNormal = 1,
    // 圖片在右,文字在左
    ACEdgeInsetsBtnTypeRight ,
    // 圖片在上,文字在下
    ACEdgeInsetsBtnTop
};
複製代碼

傳入對應的參數,直接生成指望的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上,地址

相關文章
相關標籤/搜索