??2019-10-20
不管是什么應(yīng)用 UIButton 在項(xiàng)目中的使用都是必不可少的,很多時(shí)候我們會(huì)同時(shí)給 UIButton 添加上 image 和 title 以及在 image 和 title 間留一些間距, 再有時(shí)候可能還希望其左邊或者是右邊留有有一些間距,這樣 UI 顯得更加美觀??赡艿牟季挚雌饋?lái)像這樣:




盡管這看起來(lái)很容易實(shí)現(xiàn),只要合理的設(shè)置 UIButton 的 titleEdgeInsets、imageEdgeInsets、contentEdgeInsets 等屬性就可以實(shí)現(xiàn)這些效果,但是不得不說(shuō)還是有很多開發(fā)者不確定如何控制這幾個(gè)屬性,而導(dǎo)致項(xiàng)目相關(guān)的地方出現(xiàn)了一些問(wèn)題,所以接下來(lái)增對(duì)個(gè)人的理解對(duì)這幾個(gè)屬性的設(shè)置進(jìn)行一些說(shuō)明。
UIButton 中 imageView 與 titleLabel 的布局
在 UIButton 的內(nèi)部布局中假設(shè)有個(gè)參考線,imageView 與 titleLabel 的布局均以該參考線為參照左右上下進(jìn)行偏移,最終實(shí)現(xiàn)在 image 與 title 間添加間距 imageTextSpacing 的布局。(注意這里的參考線很重要)

開始舉例前的初始代碼如下:
let button = UIButton()
var titleInsets = button.titleEdgeInsets
var imageInsets = button.imageEdgeInsets
var contentInsets = button.contentEdgeInsets
var buttonSize = button.sizeThatFits(CGSize.zero)
let imageTextSpacing: CGFloat = 0 // image 與 title 的間距
let titleSize = button.titleLabel!.sizeThatFits(CGSize.zero)
let imageSize = button.imageView!.sizeThatFits(CGSize.zero)
func reset() {
titleInsets = button.titleEdgeInsets
imageInsets = button.imageEdgeInsets
contentInsets = button.contentEdgeInsets
buttonSize = button.sizeThatFits(CGSize.zero)
}
一、 左 image 右 title 的布局
-
左
image右title→ 居中對(duì)齊 (默認(rèn)), 參考線為中線 ━━┇━━布局實(shí)現(xiàn):在當(dāng)前布局上以中線為參考線那么需將
image左移imageTextSpacing / 2,title右移imageTextSpacing / 2,buttonSize.width增加imageTextSpacing即可example(of: "(左 image 右 title) → 居中對(duì)齊 (默認(rèn)) + 間距") { imageInsets.left = -imageTextSpacing / 2.0 imageInsets.right = imageTextSpacing / 2.0 titleInsets.left = imageTextSpacing / 2.0 titleInsets.right = -imageTextSpacing / 2.0 buttonSize.width += imageTextSpacing }
-
左
image右title→ 左對(duì)齊, 參考線在左側(cè) ┇━━━━在當(dāng)前布局上
image由于默認(rèn)就是貼近左側(cè)線所以保持不變,title右移imageTextSpacing,buttonSize.width增加imageTextSpacing即可example(of: "(左 image 右 title) → 左對(duì)齊 + 間距") { button.contentHorizontalAlignment = .left titleInsets.left = imageTextSpacing titleInsets.right = -imageTextSpacing imageInsets = UIEdgeInsets.zero buttonSize.width += imageTextSpacing }
-
左
image右title→ 右對(duì)齊, 參考線在右側(cè) ┇━━━━在當(dāng)前布局上
title由于默認(rèn)就是貼近右側(cè)所以保持不變,image左移imageTextSpacing,buttonSize.width增加imageTextSpacing即可example(of: "(左 image 右 title) → 右對(duì)齊 + 間距") { button.contentHorizontalAlignment = .right titleInsets = UIEdgeInsets.zero imageInsets.left = -imageTextSpacing imageInsets.right = imageTextSpacing buttonSize.width += imageTextSpacing }
二、 左 title 右 image 的布局
首先需要實(shí)現(xiàn)的是左邊 title 右邊 image的布局, 然后再添加間距
-
左
title右image→ 居中對(duì)齊 (默認(rèn)) + 無(wú)間距在初始布局上將
title左移imageSize.width,image右移titleSize.width即可達(dá)到效果example(of: "左 title 右 image → 居中對(duì)齊 (默認(rèn)) + 無(wú)間距") { titleInsets.left = -imageSize.width titleInsets.right = imageSize.width imageInsets.left = titleSize.width imageInsets.right = titleSize.width }

-
左
title右image→ 居中對(duì)齊 (默認(rèn)) + 間距, 參考線在中間 ━━┇━━在左
title右image基礎(chǔ)上,title左移imageTextSpacing / 2.0,image右移imageTextSpacing / 2.0,buttonSize.width增加imageTextSpacing即可example(of: "(左 title 右 image) → 居中對(duì)齊 (默認(rèn)) + 間距") { titleInsets.left -= imageTextSpacing / 2.0 titleInsets.right += imageTextSpacing / 2.0 imageInsets.left += imageTextSpacing / 2.0 imageInsets.right -= imageTextSpacing / 2.0 }

-
左
title右image→ 左對(duì)齊 + 間距, 參考線在左側(cè) ┇━━━━在左
title右image基礎(chǔ)上,title貼近左側(cè)所以保持不變,image右移imageTextSpacing,buttonSize.width增加imageTextSpacing即可,效果圖與之前相同example(of: "(左 title 右 image) → 左對(duì)齊 + 間距") { imageInsets.left += imageTextSpacing imageInsets.right -= imageTextSpacing buttonSize.width += imageTextSpacing }
-
左
title右image→ 右對(duì)齊 + 間距, 參考線在右側(cè) ━━━━┇在左
title右image基礎(chǔ)上,image貼近右側(cè)所以保持不變,title左移imageTextSpacing,buttonSize.width增加imageTextSpacing即可,效果圖與之前相同example(of: "(左 title 右 image) → 右對(duì)齊 + 間距") { titleInsets.left -= imageTextSpacing titleInsets.right += imageTextSpacing buttonSize.width += imageTextSpacing }
三、上 image 下 title
首先需要實(shí)現(xiàn)的是上 image 下 title 的布局,然后再添加間距。
-
上
image下title→ 居中對(duì)齊 (默認(rèn)), 參考線在橫中線 ━╋━再初始布局的基礎(chǔ)上首先需要將
image右移titleSize.width / 2,title左移imageSize.width實(shí)現(xiàn)左右居中,因?yàn)槭巧舷虏季謪⒖季€為中間的橫線,所以需將image上移titleSize.height / 2,title下移imageSize.height / 2實(shí)現(xiàn)上下居中,修改高度為titleSize.height + imageSize.height最終達(dá)到目的。reset() example(of: "(上 image 下 title)→ 居中對(duì)齊 (默認(rèn)) + 無(wú)間距") { let titleHOffset = imageSize.width / 2.0 let imageHOffset = titleSize.width / 2.0 let titleVOffset = imageSize.height / 2.0 let imageVOffset = titleSize.height / 2.0 titleInsets = UIEdgeInsets(top: titleVOffset, left: -titleHOffset, bottom: -titleVOffset, right: titleHOffset) imageInsets = UIEdgeInsets(top: -imageVOffset, left: imageHOffset, bottom: imageVOffset, right: -imageHOffset) buttonSize.height = imageSize.height + titleSize.height }

-
上
image下title→ 居中對(duì)齊 (默認(rèn)) + 間距, 參考線在橫中線 ━╋━在當(dāng)前布局基礎(chǔ)上將
image上移mageTextSpacing / 2,title下移mageTextSpacing / 2,buttonSize.height增加imageTextSpacing即可example(of: "(上 image 下 title) → 居中對(duì)齊 (默認(rèn)) + 間距") { imageInsets.top -= imageTextSpacing / 2.0 imageInsets.bottom += imageTextSpacing / 2.0 titleInsets.top += imageTextSpacing / 2.0 titleInsets.bottom -= imageTextSpacing / 2.0 buttonSize.height += imageTextSpacing }

-
上
image下title→ 上對(duì)齊 + 間距, 參考線在頂部橫線 ┳由于是頂部對(duì)齊所以默認(rèn)
image與title都是貼近頂部,所以設(shè)置image距離頂部在0,title在當(dāng)前布局基礎(chǔ)上下移imageSize.height實(shí)現(xiàn)上(image 下 title) ,然后再將title下移imageTextSpacing實(shí)現(xiàn)間距,最后 設(shè)置buttonSize.height為imageSize.height + titleSize.height + imageTextSpacing即可。效果圖與之前相同example(of: "(上 image 下 title) → 上對(duì)齊 + 間距") { button.contentVerticalAlignment = .top imageInsets.top = 0 imageInsets.bottom = 0 titleInsets.top = imageSize.height titleInsets.bottom = -imageSize.height titleInsets.top += imageTextSpacing titleInsets.bottom -= imageTextSpacing buttonSize.height = imageSize.height + titleSize.height + imageTextSpacing } -
上
image下title→ 下對(duì)齊 + 間距, 參考線在底部橫線 ┻由于是底部對(duì)齊所以默認(rèn)
image與title都是貼近底部,所以設(shè)置title距離底部在0,image在當(dāng)前布局基礎(chǔ)上上移titleSize.height實(shí)現(xiàn)上(image 下 title) ,然后再將image上移imageTextSpacing實(shí)現(xiàn)間距,最后 設(shè)置buttonSize.height為imageSize.height + titleSize.height + imageTextSpacing即可, 效果圖與之前相同example(of: "(上 image 下 title) → 上對(duì)齊 + 間距") { button.contentVerticalAlignment = .top titleInsets.top = 0 titleInsets.bottom = 0 imageInsets.top = -titleSize.height imageInsets.bottom = titleSize.height imageInsets.top -= imageTextSpacing imageInsets.bottom += imageTextSpacing buttonSize.height = imageSize.height + titleSize.height + imageTextSpacing }
四、上下左右的邊距
到目前為止我們僅僅是在不同的布局條件下增加了 image 與 title 的間距,那么我們?nèi)绻诖嘶A(chǔ)上還需要設(shè)置 上下左右的邊距 該怎么處理呢,這里我們只需要調(diào)整之前一直沒提到的 contentEdgeInsets 屬性就可以了。這里通過(guò)設(shè)置 contentEdgeInsets 為 UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) 效果如下:

對(duì)以上進(jìn)行小結(jié)
我們應(yīng)該始終保持
titleEdgeInsets、imageEdgeInsets中的left與right同時(shí)設(shè)置,或者是top與bottom同時(shí)設(shè)置,而且是一正一負(fù),left為+,right就為-,top為+,bottom就為-,反過(guò)來(lái)亦之。應(yīng)該始終以參考線為基準(zhǔn)進(jìn)行變偏移,如果是
居中對(duì)齊那么參考線就是水平方向的中線或者是垂直方向的中線, 如果對(duì)齊方向是左右上下對(duì)齊中的一個(gè),那么參考線就是對(duì)應(yīng)左右上下的一側(cè)。從上面的例子里可以看出,對(duì)
titleEdgeInsets、imageEdgeInsets的調(diào)整不會(huì)影響UIButton的size,所以在例子中我們需要調(diào)整其size到合適。但是對(duì)contentEdgeInsets的調(diào)整會(huì)影響UIButton的size,也正因?yàn)槿绱宋覀儾趴梢酝ㄟ^(guò)contentEdgeInsets來(lái)設(shè)置左右上下某一側(cè)的邊距。
最后
示例代碼中 UIButton 在初始化的時(shí)候 size 使用約束進(jìn)行了固定,以及 UIButton 的 ImageView 及 titleLabel 的 size 也是進(jìn)過(guò)測(cè)量后寫死了,在真正項(xiàng)目應(yīng)用中應(yīng)該通過(guò)以下的方法來(lái)獲取正確的 size。
let textSize = previewButton.titleLabel?.sizeThatFits(CGSize.zero)
let imageSize = previewButton.imageView?.sizeThatFits(CGSize.zero)
示例代碼 xcode11.2