UIButton
的布局 imageView
和 titleLabel
的过程
哔哔叨: 写 iOS 代码也有段时间了, 但始终不怎么用 system
的 UIButton
, 因为每次设置 insets
的时候都会卡住好一阵啊! 各种奇怪的偏移. 趁着有时间, 整理一下.😘.
NOTE: 以下皆为伪代码
假定条件
对于一个 UIButton
的实例 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
一. 根据 contentEdgeInsets
确定布局内容大小
根据 button.contentEdgeInsets
与 button.frame
计算出子控件布局范围 contentRect
.
let contentRect = CGRect(x, y, width, height) // button.contentRect
二. 计算 imageView
与 titleLabel
的无 edgeinsets
位置
- 获取
image.size
结合布局范围contentRect
的大小得出imageView
的大小
// imageEdgetInsets = .zero 时, imageView 的 size
let defaultImgSize = CGSize(width = min(imageSize.width, contentRect.width),
height = min(imageSize.height, contentRect.height))
- 根据
imageView
的size
计算出在布局范围contentRect
内剩余的titleLabel
的布局大小以确定titleLabel
的布局大小
let remainSize = CGRect(x, y, width, height) // remainSize for titleLabel
// titleEdgeInsets = .zero 时, titleLabel 的 size
let defaultTxtSize = CGSize(width = min(textSize.width, remainSize.width),
height = textSize.height)
- 根据
button.contentHorizontalAlignment
与button.contentVerticalAlignment
决定imageView
与titleLabel
的对齐方式, 确定在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
来决定:
- 将 imageView 与 titleLabel 按照之前算好的大小左右相接,
- 将两个控件作为整体按照上述两个属性, 放在 button 的左上角, 左下角, 右对齐什么的, 具体看上述属性的值.
三. 计算加入 imageEdgeInsets
与 titleEdgeInsets
后的 result
位置
这里 edgeInsets
在这种情况下, 仅对 center
的确定有明确作用. 对 frame 的影响同时需要考虑到 intrinsicContentSize
与 contentRect
, 可以参考下述代码与UIButton的几个 edgeInsets 属性:
// 计算 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))
// 计算 apply titleEdgeInsets 之后 的 titleLabel.size
// titleLabel.height 不会被压缩
let resultTxtSize = CGSize(width = min(textSize.width, remainSize.width - titleEdgeInsets.left - titleEdgeInsets.right),
height = textSize.height)
// 计算偏移后的 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: 根据 resultSize 和 resultCenter 确定最终 frame
...
以上, 就是对 UIButton
对 button.titleLabel
与 button.imageView
的布局方式的观察结果.仅作参考. 如有不妥之处,还请 dalao 批评指正.