1. 程式人生 > IOS開發 >關於UIButton中edgeInsets的一些小細節

關於UIButton中edgeInsets的一些小細節

文章遷移自簡書 2016.09.04 14:35:01 著

# UIButton的相關屬性

UIButton提供的設定titleimage的方法,預設是圖片在左文字在右的樣式.如下圖:

而我們在開發過程中常常要調整為文字在左圖片在右,或者文字在上圖片在下等樣式.

系統同時提供了設定圖片和文字位置的edgeInsets的屬性

@property(nonatomic)          UIEdgeInsets contentEdgeInsets UI_APPEARANCE_SELECTOR; // default is UIEdgeInsetsZero
@property(nonatomic)          UIEdgeInsets titleEdgeInsets;                // default is UIEdgeInsetsZero
@property(nonatomic)          UIEdgeInsets imageEdgeInsets;                // default is UIEdgeInsetsZero
複製程式碼

提示:UI_APPEARANCE_SELECTOR 標記的屬性都支援通過外觀代理來定製。

這樣就我們就可以通過設定 titleEdgeInsetsimageEdgeInsets 來調整圖片和文字的相對位置了;

# 我們來看一下 UIEdgeInsets 的解釋

  1. 型別

    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;
    複製程式碼
  2. 設定各個值得含義

    //UIEdgeInsetsMake(<#T##top: CGFloat##CGFloat#>,<#T##left: CGFloat##CGFloat#>,<#T##bottom: CGFloat##CGFloat#>,<#T##right: CGFloat##CGFloat#>)
    testBtn.imageEdgeInsets = UIEdgeInsetsMake(0,10,40,10);
    //0 表示據原來的頂部 為0  
    //10 表示左邊框右移 10  (同理 -10 表示左邊框左移 10)
    //40 表示下邊框上移 40 
    //10 表示右邊框左移 10 (同理 -10 表示右邊框右移 10)
    複製程式碼

    總之:這些引數 正值都是表示 向相反的方向移動相應的距離(例如:對 top 正值表示向下移動,負值表示向上移動) 這樣我們就可以在 UIButton 的外部輕鬆設定想要的樣式

# 開始實現

我們現在把相關的設定抽出來做成一個 extension 的方法(一下以文字在左圖片在右進行設定)

1.新建一個 swift 檔案命名為 Button_Extension,新增程式碼如下;

import UIKit

extension UIButton {
    /// 設定文字在左圖片在右,圖文間距預設為`0.0`
    func setupImageAtRight(space: CGFloat = 0.0) {
        /*1*/
        DRPrint("獲取imageWidth")
        guard let imageWidth = imageView?.frame.size.width else {
            DRPrint("noImage")
            return
        }
        /*2*/
        DRPrint("獲取titleWidth")
        guard let titleWidth = titleLabel?.frame.size.width else {
            DRPrint("noTitle")
            return
        }

        //(備註:`button`不作為`navigationItem.titleView`是預設`titleLabel`和`imageView`距離邊界是有一個空隙的)
        titleEdgeInsets = UIEdgeInsetsMake(0.0,-(imageWidth + space * 0.5),0,imageWidth + space * 0.5)
        imageEdgeInsets = UIEdgeInsetsMake(0.0,titleWidth + space * 0.5,0.0,-(space * 0.5 + titleWidth))
        //開啟註釋就能看到普通的`button`預設`titleLabel`和`imageView`距離邊界為`0`
        //backgroundColor = UIColor.greenColor()
    }
}
複製程式碼

上述程式碼為全部操作,但是在第一次封裝中把 /*2*/ 寫在了 /*1*/ 的前面,始終得不到正確的效果.

# 存疑&解惑

此處存疑: 為什麼先獲取 titleWidthtitleWidth 值始終未 0.0 (能獲取到 titleLabel.texttitleLabel.font,字串計算 width 能正確獲得),但是當先獲取 imageWidth 時,則能獲取到正確的titleWidth ?

經過研究發現:

通過自定義一個 DRButton 並重寫 -layoutSubviews 方法發現,呼叫 imageView?.frame 時會先呼叫 -layoutSubviews,才能獲取到 imageWidth,而 titleLabel?.frame 不會呼叫 -layoutSubviews,因此先獲取 titleWidth 不會得到正確的值只會得到 0.0 .

你也可以自己寫一下

import UIKit

class TestBtn: UIButton {

    override func layoutSubviews() {
        super.layoutSubviews()
        DRPrint("button.layoutSubiews")
    }

}
複製程式碼

試試列印的結果

  1. 先獲取 imageWidth 的列印結果

    Button_extension.swift-setupImageAtRight-14:獲取imageWidth
    TestBtn.swift-layoutSubviews()-15:button.layoutSubiews
    TestBtn.swift-layoutSubviews()-15:button.layoutSubiews
    Button_extension.swift-setupImageAtRight-19:獲取titleWidth
    TestBtn.swift-layoutSubviews()-15:button.layoutSubiews
    TestBtn.swift-layoutSubviews()-15:button.layoutSubiews
    複製程式碼
  2. 先獲取titleWidth的列印結果

    Button_extension.swift-setupImageAtRight-14:獲取titleWidth
    Button_extension.swift-setupImageAtRight-19:獲取imageWidth
    TestBtn.swift-layoutSubviews()-15:button.layoutSubiews
    TestBtn.swift-layoutSubviews()-15:button.layoutSubiews
    TestBtn.swift-layoutSubviews()-15:button.layoutSubiews
    TestBtn.swift-layoutSubviews()-15:button.layoutSubiews
    複製程式碼

# 呼叫

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view,typically from a nib.

        addButton()
        //addButton("testTitle")
        //addButton("testLongLongTitle")
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}

// MARK:- 設定UI
extension ViewController {
    func addButton(title: String = "78987fded") {
        view.backgroundColor = UIColor.lightGrayColor()
        let button = TestBtn()
        button.setTitle(title,forState: .Normal)
        button.setImage(UIImage(named: "navigationbar_arrow_down"),forState: .Normal)
        button.setImage(UIImage(named: "navigationbar_arrow_up"),forState: .Selected)
        //button.setTitleColor(UIColor.grayColor(),forState: .Normal)
        button.titleLabel?.font = UIFont.systemFontOfSize(14);
        button.sizeToFit()
        button.center = view.center
        //設定圖片和標題的位置
        button.setupImageAtRight()
        button.backgroundColor = UIColor.greenColor()
        view.addSubview(button)
    }
}
複製程式碼

效果圖:

# 最後附上帶有註釋的全部程式碼

/// 設定圖片在右,圖文間隔預設為`0.0`
func setupImageAtRight(space: CGFloat = 0.0) {
    //第一種方法:通過計算字串長度來調整
    /*
    let titleString = currentTitle! as NSString
    let size = CGSizeMake(CGFloat(MAXFLOAT),bounds.size.height)
    let atttibutes = [NSFontAttributeName:titleLabel!.font]
    let titleWidth = titleString.boundingRectWithSize(size,options: .UsesLineFragmentOrigin,attributes: atttibutes,context: nil).size.width
     */
    /*
     此處存疑: 為什麼先獲取`titleWidth`則`titleWidth`值始終未`0.0`(能獲取到`titleLabel.text`和`titleLabel.font`,字串計算`width`能正確獲得),但是當先獲取`imageWidth`時,則能獲取到正確的`titleWidth`?
     疑惑解答: 通過自定義一個`DRButton`並重寫`-layoutSubviews`方法發現,呼叫`imageView?.frame`時會先呼叫`-layoutSubviews`,才能獲取到`imageWidth`,而`titleLabel?.frame`不會呼叫`-layoutSubviews`,因此先獲取`titleWidth`不會得到正確的值只會得到`0.0`
     */
    //通過獲取`titleLabel`的`width`來調整
    guard let imageWidth = imageView?.frame.size.width else {
        return
    }
    guard let titleWidth = titleLabel?.frame.size.width else {
        return
    }
    /*
    DRPrint("titleWidth = \(titleWidth)")
    DRPrint("bounds.size = \(bounds.size)")
    DRPrint("調整前_ titleLabel.frame = \(titleLabel!.frame)")
    DRPrint("調整前_ imageView.frame = \(imageView!.frame)")
    DRPrint(titleLabel?.frame.size.width)
     */

    /*
     testBtn.imageEdgeInsets = UIEdgeInsetsMake(0,10);
     0 表示據原來的頂部 為0
     10 表示左邊框右移 10  (同理 -10 表示左邊框左移 10)
     40 表示下邊框上移 40
     10 表示右邊框左移 10 (同理 -10 表示右邊框右移 10)
     備註 這些引數 正值都是表示 向相反的方向移動相應的距離
     */
    titleEdgeInsets = UIEdgeInsetsMake(0.0,imageWidth + space * 0.5)
    imageEdgeInsets = UIEdgeInsetsMake(0.0,-(space * 0.5 + titleWidth))
}
複製程式碼
// MARK:- 簡單列印
func DRPrint<T>(message: T,file: NSString = #file,lineNum: Int = #line,funcName:String = #function) ->  Void {
    #if DEBUG
    let fileName = (file as NSString).lastPathComponent
    print("\(fileName)-\(funcName)-\(lineNum):\(message)")
    #endif
}
複製程式碼

不出列印資訊的請把 #if 語句去除