Background:
When rotating the device, the view myView becomes letterboxed instead of resizing to fit the screen, see image below.
On the storyboard, centreX and centreY constraints are set.
In the code, width and height constraints are set with the function viewAddConstraints(), see code below.
After the device is rotated, I call viewAddConstraints() again, but Xcode gives the following error: Unable to simultaneously satisfy constraints.
Questions:
How do I correctly remove old constraints and then add new constraints when the device is rotated?
How do I correctly update the constraints of a view when the device is rotated?
Code:
func viewAddConstraints() {
// Width constraint full width of screen
let myViewWidth = NSLayoutConstraint(item: myView as Any, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: view.bounds.width)
myView.addConstraint(myViewWidth)
// Height constraint full height of screen
let myViewHeight = NSLayoutConstraint(item: myView as Any, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: view.bounds.height)
myView.addConstraint(myViewHeight)
}
Image:
Current situation, view is incorrectly letterboxed.
Intended situation, view resized correctly.
The proper way to set constraints to keep your subview "pinned" to the sides:
override func viewDidLoad() {
super.viewDidLoad()
let myView = UIView()
myView.backgroundColor = .systemTeal
myView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(myView)
NSLayoutConstraint.activate([
myView.topAnchor.constraint(equalTo: view.topAnchor),
myView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
myView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
myView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
}
Or, since you should be respecting the Safe Area:
override func viewDidLoad() {
super.viewDidLoad()
let myView = UIView()
myView.backgroundColor = .systemTeal
myView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(myView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
myView.topAnchor.constraint(equalTo: g.topAnchor),
myView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
myView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
myView.bottomAnchor.constraint(equalTo: g.bottomAnchor),
])
}
Now your subview will automatically resize to fit its superview on device rotation.
Related
I am trying to set a horizontal center constraint of a view with multiplier programmatically. But what I get is always a constraint with 1.0 as the multiplier. This is what I did:
private func createHalfCenteredView() {
let newView = UIView(frame: .zero)
newView.backgroundColor = .systemTeal
view.addSubview(newView)
newView.translatesAutoresizingMaskIntoConstraints = false
let top = newView.topAnchor.constraint(equalTo: view.topAnchor)
let bottom = newView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
let width = newView.widthAnchor.constraint(equalToConstant: 100)
let center = newView.centerXAnchor.constraint(equalToSystemSpacingAfter: view.centerXAnchor,
multiplier: 0.5)
NSLayoutConstraint.activate([top, bottom, width, center])
newView.setNeedsUpdateConstraints()
view.setNeedsLayout()
view.layoutIfNeeded()
}
I tried using lessThanOrEqualToSystemSpacingAfter instead of equalToSystemSpacingAfter but it is still the same. The multiplier is always 1.0 or exactly in the middle.
Can anybody help me with this? Thanks.
You can't use multipliers using helpers functions, try this way
let center = NSLayoutConstraint(item: newView, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerX, multiplier: 0.5, constant: 0)
Refer to answer
I would like to put a UIView above all other views. I saw that this is possible using the UIScreen object. However, it looks like the UIScreen doesn't have a Safe Area. This let's the View overlap with the Statusbar.
How can I place a View above all other Views while still using the Safe Area Insets?
I'm using this code in the Appdelegate's DidFinishLaunchingWithOptions():
window?.makeKeyAndVisible()
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = UIColor.red
window?.addSubview(view)
window?.bringSubview(toFront: view)
let heightConstraint = NSLayoutConstraint(item: view, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 44)
view.addConstraint(heightConstraint)
let layoutGuide = window!.safeAreaLayoutGuide
let layoutGuideConstraints = [
view.topAnchor.constraint(equalTo: layoutGuide.topAnchor, constant: 8),
view.leadingAnchor.constraint(equalTo: layoutGuide.leadingAnchor, constant: 8),
view.trailingAnchor.constraint(equalTo: layoutGuide.trailingAnchor, constant: -8)
]
NSLayoutConstraint.activate(layoutGuideConstraints)
I want to create such a UI that have two buttons stick to the bottom of the screen and a UIScrollView above them. I am using Chatto framework and would be great if anyone could give me an example how to do that based on https://github.com/badoo/Chatto/tree/master/ChattoApp/ChattoApp.
Here is the visualization of view that I'd like to have.
there is a better solution for this. you can do this by disabling the Auto Layout(button.translatesAutoresizingMaskIntoConstraints = false) property of the corresponding Button or any UIView for floating button:
Swift 4 example
button.translatesAutoresizingMaskIntoConstraints = false
if #available(iOS 11.0, *) {
button.rightAnchor.constraint(equalTo: tableView.safeAreaLayoutGuide.rightAnchor, constant: -10).isActive = true
button.bottomAnchor.constraint(equalTo: tableView.safeAreaLayoutGuide.bottomAnchor, constant: -10).isActive = true
} else {
button.rightAnchor.constraint(equalTo: tableView.layoutMarginsGuide.rightAnchor, constant: 0).isActive = true
button.bottomAnchor.constraint(equalTo: tableView.layoutMarginsGuide.bottomAnchor, constant: -10).isActive = true
}
You can easily do this using constraints. If you're using storyboard, you can set the constraints up using their "Pin" and "Align" features. If you're building it in code, you'll want to programmatically set up your constraints. Just be sure to add all the necessary constraints to fully define how the view should appear.
pseudo example with just one button:
let button = UIButton()
self.view.addsubview(button)
// pin button to bottom of superview,
let buttonBottomConstraint = NSLayoutConstraint(item: button, attribute: .Bottom, relatedBy: .Equal, toItem: self.view, attribute: .Bottom, multiplier: 1.0, constant: 0.0)
self.addConstraint(buttonBottomConstraint)
// left of superview,
// right of superview,
// and height
let scrollView = UIScrollView()
self.view.addsubview(scrollView)
// and bottom edge to top edge of button
let scrollViewBottomConstraint = NSLayoutConstraint(item: scrollView, attribute: .Bottom, relatedBy: .Equal, toItem: button, attribute: .Top, multiplier: 1.0, constant: 0.0)
self.addConstraint(scrollViewBottomConstraint)
// left of superview,
// right of superview,
// pin scrollview to top of superview,
Add a full screen scrollview then add a UIView across the bottom (anchoring to bottom of screen). Then add the 2 buttons to the UIView.
If you want to make it semi transparent then make the UIView background color to be clearColor and then add a UIView with alpha of say 0.6 and add this to your original UIView above the buttons.
I'm trying to add constraint to navigation bar, I have UIImageView, which has width, height and is centered horizontally, I want to add vertical space between UIImage and navigationBar to 0, I'm trying this for like 1 hour and couldn't figure out how, i tried adding constraint to UIView, and added constant of navbarHeight + statusBarHeight, and it worked, but I want to make relationship between imageview and navbar
let verticalSpace = NSLayoutConstraint(item: image, attribute: .Top, relatedBy: .Equal, toItem: self.view, attribute: .Top, multiplier: 1, constant: 0)
view.addConstraint(verticalSpace) // this works
try with topLayoutGuide
let verticalSpace = NSLayoutConstraint(item: image,
attribute: .Top,
relatedBy: .Equal,
toItem: self.topLayoutGuide,
attribute: .Bottom,
multiplier: 1, constant: 0)
The above constraint explanation:
simply its called: vertical space between image.Top & self.topLayoutGuide.Bottom = 0
that means Top constraint of image view attached with a Bottom attribute of topLayoutGuide with constant 0.
You can use anchors as well to make this possible for iOS 10+
if #available(iOS 11.0, *) {
image.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
} else {
image.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor).isActive = true
}
llkenny's answer for iOS 11.0+ :
image.topAnchor.constraint(equalTo:
view.safeAreaLayoutGuide.topAnchor).isActive = true
With anchors:
image.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor).isActive = true
In storyboard. Two constraints:
The first:
The second:
Result:
code:
func mainCollectionViewConstraint() {
NSLayoutConstraint.activate([
mainCollectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 10),
mainCollectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
mainCollectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
mainCollectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])
}
I am attempting to get a UIView courseView to autolayout. I would like to have the proportions of the UIView remain and fill up until the outermost edges are 15 point from the edge of the superview.
For some reason courseView fills the entire superview (minus the 15 points) and does not resize to fit. So some of it does not show and is cut off.
self.view.addSubview(courseView!)
let aspectConstraint = NSLayoutConstraint(item: courseView,
attribute: .Height,
relatedBy: .Equal,
toItem: courseView,
attribute: .Width,
multiplier: courseView.frame.size.height / courseView.frame.size.width,
constant: 0.0)
aspectConstraint.active = true
let topConstraint = courseView.topAnchor.constraintGreaterThanOrEqualToAnchor(topLayoutGuide.bottomAnchor, constant: 15)
topConstraint.active = true
let leadingConstraint = courseView.leadingAnchor.constraintLessThanOrEqualToAnchor(view.leadingAnchor, constant: 15)
leadingConstraint.active = true
let trailingConstraint = courseView.trailingAnchor.constraintGreaterThanOrEqualToAnchor(view.trailingAnchor, constant: -15)
trailingConstraint.active = true
let bottomConstraint = courseView.bottomAnchor.constraintLessThanOrEqualToAnchor(view.bottomAnchor, constant: -15)
bottomConstraint.active = true
Any ideas? Thanks!
Just disable translatesAutoresizingMaskIntoConstraints before adding the constraints and it should work just fine.
courseView.translatesAutoresizingMaskIntoConstraints = false;
And by the way, you do not need the aspectConstraint because that will most probably break the constratints (it did for me, when I tried).