關於UIButton中edgeInsets的一些小細節
文章遷移自簡書 2016.09.04 14:35:01 著
# UIButton
的相關屬性
UIButton
提供的設定title
和image
的方法,預設是圖片在左文字在右的樣式.如下圖:
而我們在開發過程中常常要調整為文字在左圖片在右,或者文字在上圖片在下等樣式.
系統同時提供了設定圖片和文字位置的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
標記的屬性都支援通過外觀代理來定製。
這樣就我們就可以通過設定 titleEdgeInsets
和 imageEdgeInsets
來調整圖片和文字的相對位置了;
# 我們來看一下 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; 複製程式碼
-
設定各個值得含義
//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*/
的前面,始終得不到正確的效果.
# 存疑&解惑
此處存疑: 為什麼先獲取
titleWidth
則titleWidth
值始終未0.0
(能獲取到titleLabel.text
和titleLabel.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")
}
}
複製程式碼
試試列印的結果
-
先獲取
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 複製程式碼
-
先獲取
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
語句去除