UIButton 高度和自动布局
UIButton heights and Auto Layout
在我的一个 ViewController 中,我使用底部有两个按钮的 UIView。显然,我想适应屏幕的两个按钮,所以我使用这个:
leftButton.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -16).isActive = true
rightButton.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -16).isActive = true
//16 points between the buttons
leftButton.rightAnchor.constraint(equalTo: rightButton.leftAnchor, constant: -16).isActive = true
//right alignment for the buttons
rightButton.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -16).isActive = true
//left padding for the left button - 16 points
leftButton.leftAnchor.constraint(greaterThanOrEqualTo: self.leftAnchor, constant: 16).isActive = true
//rightButton can't be less wide than 1/3 of its superview
rightButton.widthAnchor.constraint(greaterThanOrEqualToConstant: widthOfTheView / 3).isActive = true
它可以工作,但是如果我的左按钮标题很长怎么办?我的左键的高度变大了,没关系。但是我的右键还是不是很高,略显难看。
好的,但是如果我告诉自动布局我希望右侧按钮的高度与左侧按钮的高度相同怎么办?
rightButton.heightAnchor.constraint(equalTo: leftButton.heightAnchor, constant: 0).isActive = true
我认为这是一个解决方案,但我得到的却是这个:
从技术上讲,他们的身高是相等的,但这不是我想要的结果。
我想,也许,问题出在按钮内的 UIEdgeInsets 中,但事实并非如此(我杀死了这一行,结果是一样的)。
我想我不能选择两个按钮之间的最大高度并将其用作约束的常量,因为在这个阶段它们各自的框架为零。
我尝试使用另一个 UIView 作为这两个按钮的容器,但结果是一样的。
我可以通过 turning-off 当然可以通过自动布局和计算按钮大小和框架来解决这个问题,但我现在不想这样做。
那么,我做错了什么?
更改下面的行
leftButton.rightAnchor.constraint(equalTo: rightButton.leftAnchor, constant: -16).isActive = true
到
leftButton.rightAnchor.constraint(greaterThanOrEqualTo: rightButton.leftAnchor, constant: -16).isActive = true
可以设置
rightButton.heightAnchor.constraint(equalTo: leftButton.heightAnchor).isActive = true
目前 leftButton 的右锚点正在尝试 Fullfill -16 常量,由于左按钮正在拉伸,使用 greaterOrEqual 会增加按钮之间的距离,但尺寸会很好。
这可以通过 UIStackView
、设置
轻松完成
.axis = .horizontal
.alignment = .fill
.distribution = .fillEqually
.spacing = 12
尺寸变化时自动调整:
代码如下:
class MultilineRoundedButton: UIButton {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
func commonInit() -> Void {
self.titleLabel?.numberOfLines = 0
self.titleLabel?.textAlignment = .center
self.setContentHuggingPriority(UILayoutPriority.defaultLow + 1, for: .vertical)
self.setContentHuggingPriority(UILayoutPriority.defaultLow + 1, for: .horizontal)
self.layer.cornerRadius = 8
}
override var intrinsicContentSize: CGSize {
let size = self.titleLabel!.intrinsicContentSize
return CGSize(width: size.width + contentEdgeInsets.left + contentEdgeInsets.right, height: size.height + contentEdgeInsets.top + contentEdgeInsets.bottom)
}
override func layoutSubviews() {
super.layoutSubviews()
titleLabel?.preferredMaxLayoutWidth = self.titleLabel!.frame.size.width
}
}
class TwoButtonsView: UIView {
let theStackView: UIStackView = {
let v = UIStackView()
v.translatesAutoresizingMaskIntoConstraints = false
v.axis = .horizontal
v.alignment = .fill
v.distribution = .fillEqually
v.spacing = 16
return v
}()
let leftButton: MultilineRoundedButton = {
let v = MultilineRoundedButton()
v.translatesAutoresizingMaskIntoConstraints = false
v.setTitle("Really (really!) long first button title", for: .normal)
v.backgroundColor = UIColor(red: 184.0 / 255.0, green: 233.0 / 255.0, blue: 133.0 / 255.0, alpha: 1.0)
return v
}()
let rightButton: MultilineRoundedButton = {
let v = MultilineRoundedButton()
v.translatesAutoresizingMaskIntoConstraints = false
v.setTitle("Second title", for: .normal)
v.backgroundColor = UIColor(red: 74.0 / 255.0, green: 143.0 / 255.0, blue: 226.0 / 255.0, alpha: 1.0)
return v
}()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit() -> Void {
leftButton.titleLabel?.font = UIFont.systemFont(ofSize: 24.0, weight: .bold)
rightButton.titleLabel?.font = leftButton.titleLabel?.font
addSubview(theStackView)
theStackView.addArrangedSubview(leftButton)
theStackView.addArrangedSubview(rightButton)
NSLayoutConstraint.activate([
theStackView.topAnchor.constraint(equalTo: topAnchor),
theStackView.bottomAnchor.constraint(equalTo: bottomAnchor),
theStackView.leadingAnchor.constraint(equalTo: leadingAnchor),
theStackView.trailingAnchor.constraint(equalTo: trailingAnchor),
])
}
}
class TwoButtonViewController: UIViewController {
let buttonsView: TwoButtonsView = {
let v = TwoButtonsView()
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(buttonsView)
NSLayoutConstraint.activate([
buttonsView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20.0),
buttonsView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 20.0),
buttonsView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -20.0),
])
}
}
这个答案是@DonMag答案的延续,如果你想要一个方便的扩展来根据其中的文本调整 UIButton 的大小,那么使用下面的代码:-
extension UIButton {
func wrapContentHeight(constantValue : CGFloat) -> CGFloat {
self.titleLabel?.text = self.title(for: .normal)
self.titleLabel?.numberOfLines = 0
self.titleLabel?.frame.size.width = self.frame.width
self.titleLabel?.lineBreakMode = .byWordWrapping
self.superview?.layoutIfNeeded()
let height = self.titleEdgeInsets.top + self.titleEdgeInsets.bottom + (self.titleLabel?.frame.height ?? 45)
if height < constantValue {return constantValue}
return height
}
您可以将上述扩展应用于按钮的高度限制,如下所示:-
btnHeight.constant = selectionBtn.wrapContentHeight(constantValue: 45)
在我的一个 ViewController 中,我使用底部有两个按钮的 UIView。显然,我想适应屏幕的两个按钮,所以我使用这个:
leftButton.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -16).isActive = true
rightButton.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -16).isActive = true
//16 points between the buttons
leftButton.rightAnchor.constraint(equalTo: rightButton.leftAnchor, constant: -16).isActive = true
//right alignment for the buttons
rightButton.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -16).isActive = true
//left padding for the left button - 16 points
leftButton.leftAnchor.constraint(greaterThanOrEqualTo: self.leftAnchor, constant: 16).isActive = true
//rightButton can't be less wide than 1/3 of its superview
rightButton.widthAnchor.constraint(greaterThanOrEqualToConstant: widthOfTheView / 3).isActive = true
它可以工作,但是如果我的左按钮标题很长怎么办?我的左键的高度变大了,没关系。但是我的右键还是不是很高,略显难看。
好的,但是如果我告诉自动布局我希望右侧按钮的高度与左侧按钮的高度相同怎么办?
rightButton.heightAnchor.constraint(equalTo: leftButton.heightAnchor, constant: 0).isActive = true
我认为这是一个解决方案,但我得到的却是这个:
从技术上讲,他们的身高是相等的,但这不是我想要的结果。
我想,也许,问题出在按钮内的 UIEdgeInsets 中,但事实并非如此(我杀死了这一行,结果是一样的)。
我想我不能选择两个按钮之间的最大高度并将其用作约束的常量,因为在这个阶段它们各自的框架为零。
我尝试使用另一个 UIView 作为这两个按钮的容器,但结果是一样的。
我可以通过 turning-off 当然可以通过自动布局和计算按钮大小和框架来解决这个问题,但我现在不想这样做。
那么,我做错了什么?
更改下面的行
leftButton.rightAnchor.constraint(equalTo: rightButton.leftAnchor, constant: -16).isActive = true
到
leftButton.rightAnchor.constraint(greaterThanOrEqualTo: rightButton.leftAnchor, constant: -16).isActive = true
可以设置
rightButton.heightAnchor.constraint(equalTo: leftButton.heightAnchor).isActive = true
目前 leftButton 的右锚点正在尝试 Fullfill -16 常量,由于左按钮正在拉伸,使用 greaterOrEqual 会增加按钮之间的距离,但尺寸会很好。
这可以通过 UIStackView
、设置
.axis = .horizontal
.alignment = .fill
.distribution = .fillEqually
.spacing = 12
尺寸变化时自动调整:
代码如下:
class MultilineRoundedButton: UIButton {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
func commonInit() -> Void {
self.titleLabel?.numberOfLines = 0
self.titleLabel?.textAlignment = .center
self.setContentHuggingPriority(UILayoutPriority.defaultLow + 1, for: .vertical)
self.setContentHuggingPriority(UILayoutPriority.defaultLow + 1, for: .horizontal)
self.layer.cornerRadius = 8
}
override var intrinsicContentSize: CGSize {
let size = self.titleLabel!.intrinsicContentSize
return CGSize(width: size.width + contentEdgeInsets.left + contentEdgeInsets.right, height: size.height + contentEdgeInsets.top + contentEdgeInsets.bottom)
}
override func layoutSubviews() {
super.layoutSubviews()
titleLabel?.preferredMaxLayoutWidth = self.titleLabel!.frame.size.width
}
}
class TwoButtonsView: UIView {
let theStackView: UIStackView = {
let v = UIStackView()
v.translatesAutoresizingMaskIntoConstraints = false
v.axis = .horizontal
v.alignment = .fill
v.distribution = .fillEqually
v.spacing = 16
return v
}()
let leftButton: MultilineRoundedButton = {
let v = MultilineRoundedButton()
v.translatesAutoresizingMaskIntoConstraints = false
v.setTitle("Really (really!) long first button title", for: .normal)
v.backgroundColor = UIColor(red: 184.0 / 255.0, green: 233.0 / 255.0, blue: 133.0 / 255.0, alpha: 1.0)
return v
}()
let rightButton: MultilineRoundedButton = {
let v = MultilineRoundedButton()
v.translatesAutoresizingMaskIntoConstraints = false
v.setTitle("Second title", for: .normal)
v.backgroundColor = UIColor(red: 74.0 / 255.0, green: 143.0 / 255.0, blue: 226.0 / 255.0, alpha: 1.0)
return v
}()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit() -> Void {
leftButton.titleLabel?.font = UIFont.systemFont(ofSize: 24.0, weight: .bold)
rightButton.titleLabel?.font = leftButton.titleLabel?.font
addSubview(theStackView)
theStackView.addArrangedSubview(leftButton)
theStackView.addArrangedSubview(rightButton)
NSLayoutConstraint.activate([
theStackView.topAnchor.constraint(equalTo: topAnchor),
theStackView.bottomAnchor.constraint(equalTo: bottomAnchor),
theStackView.leadingAnchor.constraint(equalTo: leadingAnchor),
theStackView.trailingAnchor.constraint(equalTo: trailingAnchor),
])
}
}
class TwoButtonViewController: UIViewController {
let buttonsView: TwoButtonsView = {
let v = TwoButtonsView()
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(buttonsView)
NSLayoutConstraint.activate([
buttonsView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20.0),
buttonsView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 20.0),
buttonsView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -20.0),
])
}
}
这个答案是@DonMag答案的延续,如果你想要一个方便的扩展来根据其中的文本调整 UIButton 的大小,那么使用下面的代码:-
extension UIButton {
func wrapContentHeight(constantValue : CGFloat) -> CGFloat {
self.titleLabel?.text = self.title(for: .normal)
self.titleLabel?.numberOfLines = 0
self.titleLabel?.frame.size.width = self.frame.width
self.titleLabel?.lineBreakMode = .byWordWrapping
self.superview?.layoutIfNeeded()
let height = self.titleEdgeInsets.top + self.titleEdgeInsets.bottom + (self.titleLabel?.frame.height ?? 45)
if height < constantValue {return constantValue}
return height
}
您可以将上述扩展应用于按钮的高度限制,如下所示:-
btnHeight.constant = selectionBtn.wrapContentHeight(constantValue: 45)