UIButton 的布局 imageView 和 titleLabel 的過(guò)程
嗶嗶叨: 寫 iOS 代碼也有段時(shí)間了, 但始終不怎么用 system 的 UIButton, 因?yàn)槊看卧O(shè)置 insets 的時(shí)候都會(huì)卡住好一陣啊! 各種奇怪的偏移. 趁著有時(shí)間, 整理一下.??.
NOTE: 以下皆為偽代碼
假定條件
對(duì)于一個(gè) UIButton 的實(shí)例 button, 我們假定以下變量
let imageEdgeInsets = UIEdgeInsetsMake(top, left, bottom, right) // imageInsets
let titleEdgeInsets = UIEdgeInsetsMake(top, left, bottom, right) // titleEdgeInsets
let imageSize = CGSize(width, height) // image.size
let textSize = CGSize(width, height) // labelSize to fit he title text
一. 根據(jù) contentEdgeInsets 確定布局內(nèi)容大小
根據(jù) button.contentEdgeInsets 與 button.frame 計(jì)算出子控件布局范圍 contentRect .
let contentRect = CGRect(x, y, width, height) // button.contentRect
二. 計(jì)算 imageView 與 titleLabel 的無(wú) edgeinsets 位置
- 獲取
image.size結(jié)合布局范圍contentRect的大小得出imageView的大小
// imageEdgetInsets = .zero 時(shí), imageView 的 size
let defaultImgSize = CGSize(width = min(imageSize.width, contentRect.width),
height = min(imageSize.height, contentRect.height))
- 根據(jù)
imageView的size計(jì)算出在布局范圍contentRect內(nèi)剩余的titleLabel的布局大小以確定titleLabel的布局大小
let remainSize = CGRect(x, y, width, height) // remainSize for titleLabel
// titleEdgeInsets = .zero 時(shí), titleLabel 的 size
let defaultTxtSize = CGSize(width = min(textSize.width, remainSize.width),
height = textSize.height)
- 根據(jù)
button.contentHorizontalAlignment與button.contentVerticalAlignment決定imageView與titleLabel的對(duì)齊方式, 確定在imageEdgeInsets = .zero且titleEdgeInsets = .zero的情況下的imageView.frame以及titleLabel.frame.
let defaultImgRect = CGRect(x, y, width, height) // imageView.defaultRect
let defaultImgCenter = CGPoint(x, y)
let defaultTxtRect = CGRect(x, y, width, height) // imageView.defaultRect
let defaultTxtCenter = CGPoint(x, y)
這里 defaultImgRect 以及 defaultTxtRect 由 button 的 contentHorizontalAlignment 和 contentVerticalAlignment 來(lái)決定:
- 將 imageView 與 titleLabel 按照之前算好的大小左右相接,
- 將兩個(gè)控件作為整體按照上述兩個(gè)屬性, 放在 button 的左上角, 左下角, 右對(duì)齊什么的, 具體看上述屬性的值.
三. 計(jì)算加入 imageEdgeInsets 與 titleEdgeInsets 后的 result 位置
這里 edgeInsets 在這種情況下, 僅對(duì) center 的確定有明確作用. 對(duì) frame 的影響同時(shí)需要考慮到 intrinsicContentSize 與 contentRect, 可以參考下述代碼與UIButton的幾個(gè) edgeInsets 屬性:
// 計(jì)算 apply imageEdgeInsets 之后 的 imageView.size
let resultImgSize = CGSize(width = min(imageSize.width, contentRect.width - imageEdgeInsets.left - imageEdgeInsets.right),
height = min(imageSize.height, contentRect.height - imageEdgeInsets.top - imageEdgeInsets.bottom))
// 計(jì)算 apply titleEdgeInsets 之后 的 titleLabel.size
// titleLabel.height 不會(huì)被壓縮
let resultTxtSize = CGSize(width = min(textSize.width, remainSize.width - titleEdgeInsets.left - titleEdgeInsets.right),
height = textSize.height)
// 計(jì)算偏移后的 imageView 和 titleLabel 的 center
let resultImgCenter = CGPoint(x = defaultImgCenter.x + (imageEdgeInsets.left-imageEdgeInsets.right)/2,
y = defaultImgCenter.y + (imageEdgeInsets.top-imageEdgeInsets.bottom)/2)
let resultImgCenter = CGPoint(x = defaultImgCenter.x + (titleEdgeInsets.left-titleEdgeInsets.right)/2,
y = defaultImgCenter.y + (titleEdgeInsets.top-titleEdgeInsets.bottom)/2)
// last: 根據(jù) resultSize 和 resultCenter 確定最終 frame
...
以上, 就是對(duì) UIButton 對(duì) button.titleLabel 與 button.imageView 的布局方式的觀察結(jié)果.僅作參考. 如有不妥之處,還請(qǐng) dalao 批評(píng)指正.