UIButton 不完全按照 XIB 的 contentVerticalAlignment 值呈现
UIButton not render exactly as XIB's contentVerticalAlignment value
我想让按钮的顶部与按钮的 titleLabel 的顶部对齐,所以我在 xib 中将内容垂直对齐设置为顶部。这在 xib 中运行良好,但在构建之后,titleLabel 似乎仍然采用居中垂直对齐方式进行布局。我错过了什么?
首先,评论:您在 Storyboard / Interface Builder 中看到的内容:
- 并不总是完全 UIKit 在模拟器中呈现的内容
- 这并不总是 完全 UIKit 在设备上呈现的内容
这就是我们在不同的模拟器和设备上测试、测试、测试...的原因。
那么,这是怎么回事?
UIKit 使用按钮的框架 .titleLabel
来确定按钮的固有大小。
对于没有明确设置宽度或高度的默认按钮,UIKit 将标题标签插入顶部和底部 6-pts
。
这里有 2 个按钮 - 都没有高度限制。 按钮 1 是 center/center 的默认内容对齐方式,按钮 2 设置为 Left/Top。按钮标题标签背景是青色的,所以我们可以很容易地看到它的框架。
故事板/IB:
运行时间:
调试视图层次结构(注意标签框架与按钮框架):
因此,使用 18 磅系统字体,标题标签高度为 21.0
... 添加 6-pts
顶部和底部,按钮框架高度为 33-pts
。
无论您将控件对齐设置为顶部/中心/底部还是填充都没有关系...UIKit 仍然采用标签高度并添加 6-pts
顶部和底部“填充”。
如何才能获得真正的顶部对齐?让我们看看几种方法。
堆栈视图中有 6 个按钮:
按钮 1 默认为 center/center,没有高度限制。
Button 2 如我们所见,控件左/上对齐...但对垂直对齐没有影响。
按钮 3 也是 Left/Top,但让我们给它一个明确的高度(我们将使用 80
使事情变得明显)。看起来更好 - 但标题标签的顶部仍添加了 6-pts
个“填充”。
现在,您可能已经在尺寸检查器面板的顶部看到了这个:
这看起来很有希望!让我们将 Title Insets Top 设置为零!
哎呀——已经是零了?!?!?!?
与内容插图相同!
事实证明,如果边缘插入是默认值,则无论如何都会添加填充。
我们可以尝试将 Title Inset Top 设置为 -6
,并且由于标签将文本垂直居中,我们还必须将 Bottom inset 设置为 6
。这行得通......但我们可能不想依赖 6
的值在未来的 iOS 版本中是准确的。
按钮 4 - 让我们尝试 Content Insets -> Bottom: 1
... 看起来我们正在路上!标签现在 top-aligned 带有按钮框架!
所以...
Button 5 - 删除 Height: 80
约束,以便我们可以使用默认按钮高度。哦!按钮框架高度现在是 title-label-height 加上 zero-top 加上 one-bottom...
按钮 6 - 最后,我们将使用 Content Insets -> Bottom: 1
和 33
(默认按钮高度)的显式高度约束。
那么...如果我们不想设置显式高度怎么办?也许我们稍后会更改标题标签的字体大小?或者我们 运行 陷入其他问题?
您可以使用自定义 UIButton
子类。
这是一个示例,标记为 @IBDesignable
这样我们就可以在 Storyboard / IB 中看到它的布局:
@IBDesignable
class MyTopLeftAlignedButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
commonInit()
}
func commonInit() {
self.contentVerticalAlignment = .top
self.contentHorizontalAlignment = .left
// if we want to see the title label frame
//titleLabel?.backgroundColor = .cyan
}
override var bounds: CGRect {
didSet {
if let v = titleLabel {
var t = titleEdgeInsets
let h = (bounds.height - v.frame.height) * 0.5
t.top = -h
t.bottom = h
titleEdgeInsets = t
}
}
}
}
编辑 - 回复评论...
如果您的目标是匹配“按钮 5”样式 - 按钮高度与标题标签高度匹配 - 最好的选择可能是...
Select 按钮,然后在 Size Inspector 面板中使用这些设置:
0.1
顶部和底部值将覆盖默认的 6-pt
Top/Bottom“填充”,将其有效减少为零。
我想让按钮的顶部与按钮的 titleLabel 的顶部对齐,所以我在 xib 中将内容垂直对齐设置为顶部。这在 xib 中运行良好,但在构建之后,titleLabel 似乎仍然采用居中垂直对齐方式进行布局。我错过了什么?
首先,评论:您在 Storyboard / Interface Builder 中看到的内容:
- 并不总是完全 UIKit 在模拟器中呈现的内容
- 这并不总是 完全 UIKit 在设备上呈现的内容
这就是我们在不同的模拟器和设备上测试、测试、测试...的原因。
那么,这是怎么回事?
UIKit 使用按钮的框架 .titleLabel
来确定按钮的固有大小。
对于没有明确设置宽度或高度的默认按钮,UIKit 将标题标签插入顶部和底部 6-pts
。
这里有 2 个按钮 - 都没有高度限制。 按钮 1 是 center/center 的默认内容对齐方式,按钮 2 设置为 Left/Top。按钮标题标签背景是青色的,所以我们可以很容易地看到它的框架。
故事板/IB:
运行时间:
调试视图层次结构(注意标签框架与按钮框架):
因此,使用 18 磅系统字体,标题标签高度为 21.0
... 添加 6-pts
顶部和底部,按钮框架高度为 33-pts
。
无论您将控件对齐设置为顶部/中心/底部还是填充都没有关系...UIKit 仍然采用标签高度并添加 6-pts
顶部和底部“填充”。
如何才能获得真正的顶部对齐?让我们看看几种方法。
堆栈视图中有 6 个按钮:
按钮 1 默认为 center/center,没有高度限制。
Button 2 如我们所见,控件左/上对齐...但对垂直对齐没有影响。
按钮 3 也是 Left/Top,但让我们给它一个明确的高度(我们将使用 80
使事情变得明显)。看起来更好 - 但标题标签的顶部仍添加了 6-pts
个“填充”。
现在,您可能已经在尺寸检查器面板的顶部看到了这个:
这看起来很有希望!让我们将 Title Insets Top 设置为零!
哎呀——已经是零了?!?!?!?
与内容插图相同!
事实证明,如果边缘插入是默认值,则无论如何都会添加填充。
我们可以尝试将 Title Inset Top 设置为 -6
,并且由于标签将文本垂直居中,我们还必须将 Bottom inset 设置为 6
。这行得通......但我们可能不想依赖 6
的值在未来的 iOS 版本中是准确的。
按钮 4 - 让我们尝试 Content Insets -> Bottom: 1
... 看起来我们正在路上!标签现在 top-aligned 带有按钮框架!
所以...
Button 5 - 删除 Height: 80
约束,以便我们可以使用默认按钮高度。哦!按钮框架高度现在是 title-label-height 加上 zero-top 加上 one-bottom...
按钮 6 - 最后,我们将使用 Content Insets -> Bottom: 1
和 33
(默认按钮高度)的显式高度约束。
那么...如果我们不想设置显式高度怎么办?也许我们稍后会更改标题标签的字体大小?或者我们 运行 陷入其他问题?
您可以使用自定义 UIButton
子类。
这是一个示例,标记为 @IBDesignable
这样我们就可以在 Storyboard / IB 中看到它的布局:
@IBDesignable
class MyTopLeftAlignedButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
commonInit()
}
func commonInit() {
self.contentVerticalAlignment = .top
self.contentHorizontalAlignment = .left
// if we want to see the title label frame
//titleLabel?.backgroundColor = .cyan
}
override var bounds: CGRect {
didSet {
if let v = titleLabel {
var t = titleEdgeInsets
let h = (bounds.height - v.frame.height) * 0.5
t.top = -h
t.bottom = h
titleEdgeInsets = t
}
}
}
}
编辑 - 回复评论...
如果您的目标是匹配“按钮 5”样式 - 按钮高度与标题标签高度匹配 - 最好的选择可能是...
Select 按钮,然后在 Size Inspector 面板中使用这些设置:
0.1
顶部和底部值将覆盖默认的 6-pt
Top/Bottom“填充”,将其有效减少为零。