I created a custom keyboard with a xib-file. It works perfectly und shows as I want to, but I get lots of warnings when debugging the keyboard.. I created all the constraints in the xib and add it as a subview to my view.
var nib = UINib(nibName: "Keyboard", bundle: nil)
let objects = nib.instantiateWithOwner(self, options: nil)
mainView = objects.first as UIView
view.addSubview(mainView)
I do this in the viewDidLoad()-method. In the viewDidAppear() I call this to get my height:
let heightConstraint = NSLayoutConstraint(item: self.view, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 0.0, constant: 180.0)
view.addConstraint(heightConstraint)
mainView.frame = view.bounds
But now I get this warnings:
2014-09-17 12:21:21.689 Hodor[6629:1086407] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
(
"<NSLayoutConstraint:0x178087e40 'UIView-Encapsulated-Layout-Height' V:[UIInputView:0x15e607830(264)]>",
"<NSLayoutConstraint:0x1780882f0 V:[UIInputView:0x15e607830(180)]>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x178087e40 'UIView-Encapsulated-Layout-Height' V:[UIInputView:0x15e607830(264)]>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
2014-09-17 12:21:21.693 Hodor[6629:1086407] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
(
"<NSLayoutConstraint:0x178087e40 'UIView-Encapsulated-Layout-Height' V:[UIInputView:0x15e607830(264)]>",
"<NSLayoutConstraint:0x1780882f0 V:[UIInputView:0x15e607830(180)]>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x178087e40 'UIView-Encapsulated-Layout-Height' V:[UIInputView:0x15e607830(264)]>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
2014-09-17 12:21:21.697 Hodor[6629:1086407] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
(
"<NSLayoutConstraint:0x178087e40 'UIView-Encapsulated-Layout-Height' V:[UIInputView:0x15e607830(264)]>",
"<NSLayoutConstraint:0x1780882f0 V:[UIInputView:0x15e607830(180)]>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x178087e40 'UIView-Encapsulated-Layout-Height' V:[UIInputView:0x15e607830(264)]>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
What does this mean? From the text of the warnings it seems to be something with the height constraint...
Try this
view.setTranslatesAutoresizingMaskIntoConstraints(false)
view.addConstraint(NSLayoutConstraint(
item:mainView, attribute:.Left,
relatedBy:.Equal, toItem:view,
attribute:NSLayoutAttribute.Left,multiplier:0.6, constant: 0))
view.addConstraint(NSLayoutConstraint(
item:mainView, attribute:.Right,
relatedBy:.Equal, toItem:view,
attribute:.RightMargin,multiplier:0.6, constant: 0))
view.addConstraint(NSLayoutConstraint(
item:mainView, attribute:.Width,
relatedBy:.Equal, toItem:view,
attribute: .Width,multiplier:0.6, constant: 0))
view.addConstraint(NSLayoutConstraint(
item:mainView, attribute:.Height,
relatedBy:.Equal, toItem:view,
attribute:.Height,multiplier:0.6, constant: 0))
There are some conflicts with constraints. Autoresize is conflicting with constraints you have applied.
Related
I am learning about layout constraints and find it a bit confusing why the last line of NSLayout Constraints for the trailing anchor mentions a view instead of loginView? Is there any good logical way to think of this? Struggling to imagine what is written.
let loginView = LoginView()
view.addSubview(loginView)
NSLayoutConstraint.activate([
loginView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
loginView.leadingAnchor.constraint(equalToSystemSpacingAfter: view.leadingAnchor, multiplier: 1),
view.trailingAnchor.constraint(equalToSystemSpacingAfter: loginView.trailingAnchor, multiplier: 1)
])
To clarify the "flipping" between:
loginView.leadingAnchor.constraint(...)
and:
view.trailingAnchor.constraint(...)
Both of these sets of constraints will give the same result:
NSLayoutConstraint.activate([
loginView.heightAnchor.constraint(equalToConstant: 120.0),
loginView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
loginView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 8.0),
loginView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -8.0),
])
NSLayoutConstraint.activate([
loginView.heightAnchor.constraint(equalToConstant: 120.0),
loginView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
loginView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 8.0),
view.trailingAnchor.constraint(equalTo: loginView.trailingAnchor, constant: 8.0),
])
In each case, we're telling auto-layout to put the trailing-edge of loginView 8-points from the trailing-edge of view.
Which approach to use really comes down to individual preference: Do I like using all Positive values, with order-flipping? Or do I like using Positive values for "left-side" constraints and Negative values for "right-side" constraints without order-flipping (obviously, flip the terminology for LTR locales).
Starting with iOS 11, Apple added the concept of system spacing - which changes based on device size, accessibility options, etc - which we can use instead of hard-coded values.
We have equalToSystemSpacingAfter (and equalToSystemSpacingBelow), but we do not have equalToSystemSpacingBefore (or equalToSystemSpacingAbove).
So, if we want to use system spacing, we must "flip" the constraint order:
NSLayoutConstraint.activate([
loginView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
loginView.heightAnchor.constraint(equalToConstant: 120.0),
loginView.leadingAnchor.constraint(equalToSystemSpacingAfter: view.leadingAnchor, multiplier: 1),
view.trailingAnchor.constraint(equalToSystemSpacingAfter: loginView.trailingAnchor, multiplier: 1),
])
The code you posted is defining a set of layout constraints for the loginView object. The constraints specify how the loginView should be positioned within its parent view.
In the last line of the code, the view.trailingAnchor is being used as the reference for the trailing edge of the loginView. This means that the loginView will be positioned such that its trailing edge is aligned with the trailing edge of the parent view.
In general, when working with layout constraints, it is important to think about the relationship between the views being constrained and the constraints themselves. In this case, the loginView is the view being constrained, and the constraints are defining how the loginView should be positioned relative to its parent view.
view means self.view. This is a UIViewController; it has a view. This is the view that will contain the loginView; you can actually see the loginView being added to the view controller's view as a subview, right there in the code.
So this code inserts the loginView into self.view and then proceeds to describe the physical relationship between their sizes and positions.
I'm using PureLayout
I'm doing all the layout by code, by the way.
I have a UIScrollView that functions like Facebook's news feed. My scroll view has some subviews. Let's call 'em "cards".
I've set my constraints inside updateConstraints as such:
for (i, view) in enumerate(self.viewsForAutoLayout) {
/** Set view dimension **/
view.autoSetDimension(ALDimension.Width, toSize: view.frame.width)
view.autoSetDimension(ALDimension.Height, toSize: view.frame.height)
/** Make 'em stack **/
if i == 0 {
view.autoPinEdgeToSuperviewEdge(
ALEdge.Top,
withInset: offset
)
} else {
view.autoPinEdge(
ALEdge.Top,
toEdge: ALEdge.Bottom,
ofView: self.viewsForAutoLayout[i - 1],
withOffset: offset
)
}
}
self.setNeedsLayout()
And in layoutSubviews, this is where I resize the cards. Their respective height is being updated after an image is loaded from the internet (via NSURLConnection.sendAsynchronousRequest, fyi, but not related to this topic.) After that, I update my scrollView's height.
for (i, view) in enumerate(self.viewsForAutoLayout) {
view.realignContent()
}
totalHeight += view.frame.size.height + offset
let size = CGSizeMake(AppConstants.ScreenSize.SCREEN_WIDTH, totalHeight)
self.scrollView.resizeContent(size) // extension
The problem is that they're not being aligned properly. It's like calling setNeedsLayout() (which eventually calls layoutSubviews() -- correct me if I've mistaken) that does not align the content at all. I've also made sure that setNeedsUpdateConstraints() is called as well.
I've tried calling this in layoutSubviews...
view.autoSetDimension(ALDimension.Height, toSize: view.frame.height)
...but I get some kind of warning:
Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this: (1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand,
refer to the documentation for the UIView property
translatesAutoresizingMaskIntoConstraints)
(
"<NSLayoutConstraint:0x1700918a0 V:[XXX.ContentBox:0x15fd63a80(928.986)]>",
"<NSLayoutConstraint:0x170098bf0 V:[XXX.ContentBox:0x15fd63a80(1275.99)]>"
)
(ContentBox is my UIView for the card)
The cards look messed up. What should I do to align my cards properly?
I'd gladly answer side comments regarding the code if it helps understand the question. :) Suggestions on the side aren't bad either.
Going by the warning you're seeing, it looks like you need to set view.translatesAutoresizingMaskIntoConstraints = false when you instantiate your card view.
I have three horizontally aligned UIViews within a container UIView. These three views should span the entire width of the above UIImageView. The problem, however, is that sometimes only one or two of the child views should be shown.
I set up my view hierarchy like so:
Since the child views are set to be equal to the width of the first child (which will always be shown), I simply set the width of the first child to be a fraction of the UIImageView width. So if three child views should be shown, the first child view would have a multiplier of 1/3 the width of the UIImageView. If two child views should be shown, the multiplier would be 1/2. If just one, the multiplier would be 1.
This seemed like a perfect solution, however the multiplier property is read only. My first attempt to solve this was by creating three different NSLayoutConstraints attached to the first child view, all with a different multiplier with 2/3 of them turned off. Then, on runtime, I would enable the appropriate constant with the appropriate multiplier based off of the number of views I wanted to show.
This resulted in a lot of ugly errors, and so did my second solution:
var constraint = NSLayoutConstraint(item: color1, attribute:NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: imageView, attribute: NSLayoutAttribute.Width, multiplier: 1/2, constant: 0)
view.addConstraint(constraint)
Where I would add a new constraint to the view based on the multiplier I wanted. This, of course, resulted in an error:
When added to a view, the constraint's items must be descendants of that view (or the view itself). This will crash if the constraint needs to be resolved before the view hierarchy is assembled.
My question, therefore, is if I can treat the constant property like the multiplier property. My fear with doing this, however, is that if I set the constant for the width of the first child view, it would not update its width when the phone rotates.
Is there a better solution for all of this?
Firstly, In your question your were using IB but you seemed to suggest there may be a different number of views each time you moved to the view controller which is why I decided to create the NSLayoutConstraints programatically.
Secondly, my solution is fine provided you didn't intend to change the number of views whilst you were on the view controller. If you did, then this needs a bit more work.
In your view controller:
var viewWidthConstraints : [NSLayoutConstraint] = []
override func viewDidLoad() {
super.viewDidLoad()
let numberOfViews = 3
var previousView: UIView = self.view
for i in 0..<numberOfImages {
let myView = UIView()
myView.setTranslatesAutoresizingMaskIntoConstraints(false)
myView.backgroundColor = UIColor.grayColor().colorWithAlphaComponent(CGFloat(i) * (1/CGFloat(numberOfImages)) + 0.1)
self.view.addSubview(myView)
let heightConstraint = NSLayoutConstraint.constraintsWithVisualFormat("V:|[view]|",
options: .DirectionLeadingToTrailing,
metrics: nil,
views: ["view" : myView])
let widthConstraint = NSLayoutConstraint(item: myView,
attribute: .Width,
relatedBy: .Equal,
toItem: nil,
attribute: .NotAnAttribute,
multiplier: 1.0,
constant: self.view.frame.width / CGFloat(numberOfImages))
let attribute: NSLayoutAttribute = (i == 0) ? .Left : .Right
let leftConstraint = NSLayoutConstraint(item: myView,
attribute: .Left,
relatedBy: .Equal,
toItem: previousView,
attribute: attribute,
multiplier: 1.0,
constant: 0.0)
self.view.addConstraints(heightConstraint)
self.view.addConstraint(leftConstraint)
myView.addConstraint(widthConstraint)
viewWidthConstraints.append(widthConstraint)
previousView = myView
}
}
func updateWidthConstraints() {
if viewWidthConstraints.count > 0 {
let width = self.view.frame.width / CGFloat(viewWidthConstraints.count)
for constraint in viewWidthConstraints {
constraint.constant = width
}
}
}
override func viewWillLayoutSubviews() {
updateWidthConstraints()
}
In viewDidLoad you add the UIViews to the view and set up their constraints. The vertical constraint you could change to make the UIViews appear underneath your UIImageView. And change numberOfViews to increase or decrease the number of views.
Then in viewWillLayoutSubviews you update the width of each view using their width constraint. This will make sure, if the device is rotated, each view takes up the correct proportion of the screen.
This is what is looked like with horizontal orientation.
And vertical orientation.
I'm getting the error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** +[NSLayoutConstraint constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:]: Invalid pairing of layout attributes'
Any ideas?
Here is my code:
static func addViewConstraintsCenterTop(constrainObject : UIView, toSibling : UIView) {
constrainObject.setTranslatesAutoresizingMaskIntoConstraints(false)
var constraints:[NSLayoutConstraint] = []
var constW = NSLayoutConstraint(item: constrainObject, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: toSibling, attribute: NSLayoutAttribute.Width, multiplier: 1.0, constant: 0)
constraints.append(constW)
var constH = NSLayoutConstraint(item: constrainObject, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal, toItem: toSibling, attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant: 0)
constraints.append(constH)
constrainObject.addConstraints(constraints)
}
A constraint between sibling views cannot be installed on either sibling, but must be installed on a common ancestor.
From the Installing Constraints section of Apple's Auto Layout Guide:
The view that holds the constraint must be an ancestor of the views
the constraint involves, and should usually be the closest common
ancestor. (This is in the existing NSView API sense of the word ancestor,
where a view is an ancestor of itself.) The constraint is interpreted in the coordinate system
of that view.
If you want to retain this constraint, you would need to hold on to a reference to it, and manually add it to the correct superview when your view's superview changed, ideally in your view controller's updateViewConstraints() method.
I suspect you have to add the constraints to the super view, i.e. the view that contains constrainObject and toSibling.
I want to move 2 button to center of width of screen.
It's should looks like: |<-(100)FirstButton(50)->SecondButton(100)->|
I started from first button.
var const = NSLayoutConstraint(item: firstButton,
attribute: NSLayoutAttribute.Left,
relatedBy: NSLayoutRelation.Equal,
toItem: view.superview,
attribute: NSLayoutAttribute.Left,
multiplier: 1.0,
constant: 100)
Why it doesn't work?
It seem that firstButton have not set the property translatesAutoresizingMaskIntoConstraints to false
Apple document description of translatesAutoresizingMaskIntoConstraints:
/* By default, the autoresizing mask on a view gives rise to constraints that fully determine
the view's position. This allows the auto layout system to track the frames of views whose
layout is controlled manually (through -setFrame:, for example).
When you elect to position the view using auto layout by adding your own constraints,
you must set this property to NO. IB will do this for you.
*/
#available(iOS 6.0, *)
open var translatesAutoresizingMaskIntoConstraints: Bool // Default YES
So you must set firstButton's translatesAutoresizingMaskIntoConstraints to false when you adding your own constraints