Reputation: 2809
I am building my screen in code which contains a stackView that holds my textfields. This stackView looks great on an iPhone 8/8Plus/X device, but too large on an SE screen. I realize that I need to size my stackView proportionately to the device size, but am not sure how programmatically. All the tutorials that I've seen show how to do it in IB.
Here is the code I'm working with:
let textFieldStackView = UIStackView(arrangedSubviews: [nameTextField, emailTextField, passwordTextField])
textFieldStackView.translatesAutoresizingMaskIntoConstraints = false
textFieldStackView.distribution = .fillEqually
textFieldStackView.axis = .vertical
textFieldStackView.spacing = 10
view.addSubview(textFieldStackView)
textFieldStackView.topAnchor.constraint(equalTo: logoImageView.bottomAnchor, constant: 100).isActive = true
textFieldStackView.heightAnchor.constraint(equalToConstant: 150).isActive = true
textFieldStackView.widthAnchor.constraint(equalToConstant: 350).isActive = true
textFieldStackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
It's not just the width, but the height as well which I need to grow accordingly. How would I do this in code?
Upvotes: 0
Views: 1878
Reputation: 219
If you need to clip your view to edges of you screen you should use safeArea for top/bottom constraints and leading and trailing margin for leading and trailing constraints. This way your view will look great on any device you need.
EG:
yourView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor).isActive = true
yourView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor).isActive = true
yourView.leadingAnchor.constraint(equalTo: self.view.layoutMarginsGuide.leadingAnchor).isActive = true
yourView.trailingAnchor.constraint(equalTo: self.view.layoutMarginsGuide.trailingAnchor).isActive = true
It's ios 11 code. For support of iOS 10 layout use for top and bottom anchors of controller use self.topLayoutGuide.bottomAnchor and self.bottomLayoutGuide.topAnchor.
It's much correct to use autolayout in autolayout, not UISCreenSizes as in top comment.
https://useyourloaf.com/blog/safe-area-layout-guide/ learn this guide, it's little helps.
EDIT:
If you want to have your view in the center and change its size depending on screen size and same time having equal spacing leading and trailing the best way i see is to use supporting views on the left and right ofyour view, which will have same widths. Adjusting compression resistance on center (your) view you will make it to become thinner of wider.
Something like this:
EDIT2:
Based on your comment and in code:
yourView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
yourView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
yourView.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor, constant: 10).isActive = true
yourView.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor, constant: 10).isActive = true
yourView.bottomAnchor.constraintGreaterThanOrEqualToSystemSpacingBelow(self.view.safeAreaLayoutGuide.bottomAnchor, multiplier: 1.0).isActive = true
yourView.topAnchor.constraintGreaterThanOrEqualToSystemSpacingBelow(self.view.safeAreaLayoutGuide.topAnchor, multiplier: 1.0).isActive = true
yourView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
yourView.setContentCompressionResistancePriority(.defaultLow, for: .vertical)
Upvotes: 1
Reputation: 23500
You can scale the values from one device screen (6S in the example below) to another..
func ScaleWidth(CGFloat value) -> CGFloat {
return round(value * (UIScreen.main.bounds.size.width / CGFloat(375.0)));
}
func ScaleHeight(CGFloat value) -> CGFloat {
return round(value * (UIScreen.main.bounds.size.height / CGFloat(667.0)));
}
textFieldStackView.topAnchor.constraint(equalTo: logoImageView.bottomAnchor, constant: ScaleHeight(100.0)).isActive = true
textFieldStackView.heightAnchor.constraint(equalToConstant: ScaleHeight(150.0)).isActive = true
textFieldStackView.widthAnchor.constraint(equalToConstant: ScaleWidth(350.0)).isActive = true
textFieldStackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
Another option is to use the constraint multiplier:
func AspectWidth() -> CGFloat {
return round(UIScreen.main.bounds.size.width / CGFloat(375.0));
}
func AspectHeight() -> CGFloat {
return round(UIScreen.main.bounds.size.height / CGFloat(667.0));
}
textFieldStackView.topAnchor.constraint(equalTo: logoImageView.bottomAnchor, constant: 100.0, multiplier: AspectHeight()).isActive = true
textFieldStackView.heightAnchor.constraint(equalToConstant: 150.0, multiplier: AspectHeight()).isActive = true
textFieldStackView.widthAnchor.constraint(equalToConstant: 350.0, multiplier: AspectWidth()).isActive = true
textFieldStackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
Upvotes: 1
Reputation: 164
I can't comment on an answer of kiwisip, so I have to write it here - using UIScreen.main.frame
isn't a good solution, it's better to rely on size of view, in which you add your stack view as subview. In your example, you have to replace widthAnchor
constraint with something like this:
textFieldStackView.widthAnchor.constraint(equalTo: view.widthAnchor, constant: 0).isActive = true
On the other hand - using superview sizes divided by constant instead of just using constant values can be a good idea, but it depends on your layout.
Upvotes: 0
Reputation: 447
The easiest solution (but not the prettiest one) is to use UIScreen.main.frame
to calculate the constants of your constraints. UIScreen.main.frame
represents the frame of the whole screen and is different on each device (320x568 on iPhone SE, 375x667 on iPhone 8).
Your code could look like this if you would like the stack view to look the same on each device:
let textFieldStackView = UIStackView(arrangedSubviews: [nameTextField, emailTextField, passwordTextField])
textFieldStackView.translatesAutoresizingMaskIntoConstraints = false
textFieldStackView.distribution = .fillEqually
textFieldStackView.axis = .vertical
textFieldStackView.spacing = UIScreen.main.frame.height / 66.7 // will be 10 on iPhone 8
view.addSubview(textFieldStackView)
textFieldStackView.topAnchor.constraint(equalTo: logoImageView.bottomAnchor, constant: UIScreen.main.frame.height / 6.67).isActive = true
textFieldStackView.heightAnchor.constraint(equalToConstant: UIScreen.main.frame.height / 4.45).isActive = true
textFieldStackView.widthAnchor.constraint(equalToConstant: UIScreen.main.frame.width / 1.07).isActive = true
textFieldStackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
Upvotes: 0