Reputation: 1302
In one of my ViewControllers I'm using the UIView with two buttons at the bottom. Obviously, I want to fit both of the buttons of the screen so I'm using this:
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
It works but what if I have a really long title for the left button? My left button became bigger in height, and it's ok. But my right button is still not very high, and it looks slightly ugly.
Ok, but what if I'm telling to Auto Layout that I want the right button to have the same height as the left.
rightButton.heightAnchor.constraint(equalTo: leftButton.heightAnchor, constant: 0).isActive = true
I thought it'd be a solution, but I'm getting this instead:
Technically, their heights are equal but it's not the outcome I wanted.
I thought that, maybe, problem is in UIEdgeInsets within the button but it's not the case (I killed this line, result is the same).
I think I can't choose maximum height between two buttons and use it as a constant for constraint because their respective frames are zero at this stage.
I tried to use another UIView as container for these two buttons but result is the same.
I can solve this issue by turning-off Auto Layout of course and calculating button sizes and frames but I don't want to do that for now.
So, what am I doing wrong here?
Upvotes: 2
Views: 457
Reputation: 116
This answer is the continuation of @DonMag's answer, if you want a handy extension to resize UIButton according to the text inside it then use the below code :-
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
}
and you can apply the above extention to button's height constraint like below:-
btnHeight.constant = selectionBtn.wrapContentHeight(constantValue: 45)
Upvotes: 2
Reputation: 77423
This can be done very easily with a UIStackView
, setting
.axis = .horizontal
.alignment = .fill
.distribution = .fillEqually
.spacing = 12
Automatically adjusts on size change:
Here is the code:
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),
])
}
}
Upvotes: 2
Reputation: 1396
change below line
leftButton.rightAnchor.constraint(equalTo: rightButton.leftAnchor, constant: -16).isActive = true
To
leftButton.rightAnchor.constraint(greaterThanOrEqualTo: rightButton.leftAnchor, constant: -16).isActive = true
you can set
rightButton.heightAnchor.constraint(equalTo: leftButton.heightAnchor).isActive = true
Currently leftButton's right anchor is trying to Fullfill -16 constant and due to that left button is getting stretched using greaterOrEqual will increase distancr among buttons but sizes will be good to go.
Upvotes: -1