關於UIButton中edgeInsets的一些小細節

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

# UIButton的相關屬性

UIButton提供的設置titleimage的方法,默認是圖片在左文字在右的樣式.以下圖:bash

而咱們在開發過程當中經常要調整爲文字在左圖片在右,或者文字在上圖片在下等樣式.ide

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

@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 標記的屬性都支持經過外觀代理來定製。atom

這樣就咱們就能夠經過設置 titleEdgeInsetsimageEdgeInsets 來調整圖片和文字的相對位置了;spa

# 咱們來看一下 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. 設置各個值得含義code

    //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 的外部輕鬆設置想要的樣式orm

# 開始實現

咱們如今把相關的設置抽出來作成一個 extension 的方法(一下以文字在左圖片在右進行設置)cdn

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, 40, 10); 0 表示據原來的頂部 爲0 10 表示左邊框右移 10 (同理 -10 表示左邊框左移 10) 40 表示下邊框上移 40 10 表示右邊框左移 10 (同理 -10 表示右邊框右移 10) 備註 這些參數 正值都是表示 向相反的方向移動相應的距離 */
    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))
}
複製代碼
// 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 語句去除

相關文章
相關標籤/搜索