友情提示:最終效果可跳至文章末尾Step 4處
先在View中初始化一個最基本的帶有圖片和文字的UIButton
let button = UIButton(type: .custom)
button.setImage(UIImage(named: "tab_bar_list_off"), for: .normal)
button.setImage(UIImage(named: "tab_bar_list_on"), for: .highlighted)
button.setTitle("列表", for: .normal)
button.sizeToFit()
button.backgroundColor = .red
button.center = view.center
view.addSubview(button)
此時頁面中的按鈕狀態(tài)為

初始化UIButton
UIEdgeInsets的初始化方法為
UIEdgeInsets(top: CGFloat, left: CGFloat, bottom: CGFloat, right: CGFloat)
其中包含的四個參數(shù)意義為:元素的某個邊界基于原位置將要移動的距離,正值為靠近控件矩形區(qū)域的中心,負值為遠離控件矩形區(qū)域的中心。
例如:
button.titleEdgeInsets = UIEdgeInsets(top: 10, left: 0, bottom: 0, right: -10)
意味著將按鈕的文字分別向下、向右移動了10像素,效果如下

UIEdgeInsets(top: 10, left: 0, bottom: 0, right: -10)
又例如:
button.titleEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 10)
效果顯示為

UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 10)
是因為只將文字的右側(cè)向左移動,而沒有將文字左側(cè)也做相應(yīng)移動,因此空間顯示不開,所以正確的方法應(yīng)該是
button.titleEdgeInsets = UIEdgeInsets(top: 0, left: -10, bottom: 0, right: 10)
效果如下

UIEdgeInsets(top: 0, left: -10, bottom: 0, right: 10)
由此可以清楚,若要構(gòu)建垂直布局的UIButton,應(yīng)該分一下幾步
- 分別獲取圖標的尺寸和文字的尺寸
- 將圖標和文字移動到Button的中心
- 將圖標向上移動某個高度,將文字向下移動某個高度,視需求而定
- 有必要的話,將UIButton的Frame進行重新調(diào)整
例如:
Step 1:
if let titleSize = button.titleLabel?.intrinsicContentSize, let iconSize = button.currentImage?.size {
}
Step 2: 這里對于居中的處理,我個人喜歡將圖標和文字都拉伸成按鈕的寬度,這樣從視覺上就是居中的狀態(tài)
if let titleSize = button.titleLabel?.intrinsicContentSize, let iconSize = button.currentImage?.size {
button.titleEdgeInsets = UIEdgeInsets(top: 0, left: -iconSize.width, bottom: 0, right: 0)
button.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: -titleSize.width)
}
居中后的效果:

圖標和文字居中
Step 3:
if let titleSize = button.titleLabel?.intrinsicContentSize, let iconSize = button.currentImage?.size {
let titleVerOffset = titleSize.height / 2
button.titleEdgeInsets = UIEdgeInsets(top: titleVerOffset,
left: -iconSize.width,
bottom: -titleVerOffset,
right: 0)
let iconVerOffset = iconSize.height / 2
button.imageEdgeInsets = UIEdgeInsets(top: -iconVerOffset,
left: 0,
bottom: iconVerOffset,
right: -titleSize.width)
}
效果如下,可以看到圖標和文字都已經(jīng)超過了按鈕的邊界了

上圖下字
Step 4:
if let titleSize = button.titleLabel?.intrinsicContentSize, let iconSize = button.currentImage?.size {
let titleVerOffset = titleSize.height / 2
button.titleEdgeInsets = UIEdgeInsets(top: titleVerOffset,
left: -iconSize.width,
bottom: -titleVerOffset,
right: 0)
let iconVerOffset = iconSize.height / 2
button.imageEdgeInsets = UIEdgeInsets(top: -iconVerOffset,
left: 0,
bottom: iconVerOffset,
right: -titleSize.width)
button.frame = CGRect(origin: button.frame.origin,
size: CGSize(width: button.bounds.size.width,
height: iconSize.height + titleSize.height))
}
最終效果:

上圖下字UIButton
后續(xù)可以根據(jù)不同的需求對按鈕進行進一步的調(diào)整,例如在圖標和文字中間加入一定的間距等,原理也都跳不出以上的步驟。再進一步,可以將此段代碼抽離成獨立方法,方便將來頻繁調(diào)用,在此就不多做贅述,希望對有此需求的朋友有所幫助。