Can't get constraints right in iOS8 custom keyboard - ios

I'm having major problems with auto layout in iOS 8. I'm working on a custom keyboard, but I can not get the view to display at the correct height. The rendered view on the device comes out too low, more like the intended height of the UIImageView alone. I've set the constraints as shown in the image:
But I keep getting this error in the log, that tells me nothing (probably because I don't understand where to look):
2014-12-06 19:56:45.095 [40268:12181198] 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:0x600000096940 V:[UIInputView:0x7fe780d3f600(246)]>",
"<NSLayoutConstraint:0x600000097390 'UIView-Encapsulated-Layout-Height' V:[UIInputView:0x7fe780d3f600(0)]>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x600000096940 V:[UIInputView:0x7fe780d3f600(246)]>
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.
I've tried following the code in iOS 8 Custom Keyboard: Changing the Height, but it still wont work.
Edit:
I'm setting my height constraint using the following function:
override func updateViewConstraints() {
super.updateViewConstraints()
// Add custom view sizing constraints here
let changeToOrientation = deviceInfoService.getOrientationForView(self.inputView)
if (changeToOrientation == Orientation.Portrait) {
self._heightConstraint!.constant = 246
self.inputView.addConstraint(self._heightConstraint!)
} else {
self._heightConstraint!.constant = 192
self.inputView.addConstraint(self._heightConstraint!)
}
}

This looks like a bug in the iOS Custom Keyboard implementation. The workaround currently is to adjust your constraint's priority to more/less than 1000

The error tells you that you have conflicting constraints. UIView-Encapsulated-Layout-Height seems to be a pre-defined height (When searching I mostly found references to its usage in UICollectionView). My guess is that it's the height defined by the OS that your keyboard is allowed to be. The other constraint is most likely the intrinsic content size of your created view (316 and 30 does indeed add up to 246).
My conclusion is that you probably did something wrong when you changed the height. As I'm not too familiar with keyboard extension development I can't tell you what. However when it comes to the Today Extensions which I have worked a bit with, there was a method you could override which specified the height of the widget.

This looks similar to how a TodayExtension height can be provided. Try this:
self.preferredContentSize = CGSizeMake(320, 246);
That's how I set the height of my today extension vs setting height constraints explicitly. I'll try this myself, just putting together a sample keyboard project now.

Related

Size Class'd Auto Layout: Constraint Activated For Wrong Size Class

When I set up an Auto Layout constraint customized by Size Classes, such that the constraint is installed in some Size Class(es), and not in others, I get this Unsatisfiable Constraints error message during runtime but the result is as expected on Interface Builder with no errors / warnings.
What I am testing
For this simplified example, to demonstrate how Unsatisfiable Constraints unexpectedly happen, I want the star image to have fixed height, but custom width based on Size Class. As you can see in the image, I want it fairly big for Regular Width.
Because the width constraint is not installed for Compact Width, it gets the image size based on the intrinsic content of the star image. (Credit: This star is from Use Your Loaf)
The problem
As I rotate the device from landscape to portrait in the simulator running iPhone 8 Plus (iOS 11.1 with Xcode 9.1), I get the following error:
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.
....
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x6040002839d0 UIImageView:0x7fe177d0b940.width == 480 (active)>
So apparently, this width constraint is still present even when my view is changing to Compact Width, where I specified to have the constraint NOT installed. Why is this? Is using installed the proper way to customize Auto Layout constraints based on different Size Class? I don't see any other way to achieve it in Storyboard though.
I tried creating the width constraint programmatically and setting .isActive from inside traitCollectionDidChange instead, and I still got the same error.
I didn't get such error when testing the sample code from Mysteries of Auto Layout WWDC talk, which is deactivating / activating constraints through traitCollectionDidChange just like I did.
Update
I added my own code on GitHub.
You can silence this error by lowering the priority of the width constraint to 999.
I believe this warning has something to do with UIView-Encapsulated-Layout-Width constraint which is added automatically by the autolayout engine. There is a lot of questions regarding this type of constraint but I haven't found any good answer what it is and what is it's role. The best I found is this thread.
<NSLayoutConstraint:0x60400008d110 'UIView-Encapsulated-Layout-Width' UIView:0x7ff1e5f08d20.width == 414 (active)>
If you will take a look at the preview of your layout in the view hierarchy debugger you will see that all the constraints are set up as expected in both orientations.
I had a similar problem where I tried switching between two constraints that were conflicting. My problem was that I was activating the conflicting constraint while the other was active as well and I would get this warning.
Try deactivating the conflicting active constraint first and then activating the new constraint. That will get rid of the warning.
if isWide {
wideConstraint.isActive = false
narrowConstraint.isActive = true
else {
narrowConstraint.isActive = false
wideConstraint.isActive = true
}

Disabling Auto Layout For A Single UIImageView

I have a UIImageView in my photo app that changes size based upon the imported image. The size is created dynamically and then positioned programmatically in the center of the screen without using auto layout.
Now to do this I have used
self.imageViewCanvas.translatesAutoresizingMaskIntoConstraints = YES;
[self.imageViewCanvas setFrame:CGRectMake(0, 0, screenWidth, screenHeight)];
to disable the constraints. The problem is that whenever I load the app I get a long warning message that contains the following:
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)
(
"",
""
)
Will attempt to recover by breaking constraint
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in may also be helpful.
Is there a way to disable this particular warning or can I adjust the code in some way to avoid it?
Thanks
For a storyboard or a NIB with auto layout enabled, Xcode will provide constraints that are missing. This happens at build time. If you look at the Size inspector for a view to which you haven't added any constraints, you'll see a message about Xcode adding constraints for you. So, basically, no matter what you do, the image view will be constrained.
If you then set translatesAutoresizingMaskIntoConstraints to true, that will conflict with the automatically-supplied constraints. You could remove/deactivate those constraints, but it's hard to obtain references to them.
Your options:
Use auto layout to position the image view. It's easy to accomplish what you want.
Turn off auto layout for the whole NIB, so there will be no constraints and translatesAutoresizingMaskIntoConstraints will be true by default.
Add sufficient constraints of your own in the NIB but mark them to be removed at build time on the Attributes or Size inspector. This signals to Xcode that you want to take over and prevents it from supplying its own constraints. (This is normally done when you will be supplying constraints programmatically, but it can also work if you're just going to turn on translatesAutoresizingMaskIntoConstraints.)
Add constraints of your own and set up outlets to them. Deactivate them programmatically before turning on translatesAutoresizingMaskIntoConstraints.

Using auto layout and systemLayoutSizeFittingSize

I have a really simple view that I cannot for the life of me figure out the problem. I have successfully used auto layout and systemLayoutSizeFittingSize for fairly complex custom UITableViewCells. This is my first time using it for a UIView (to be used in a UITableView.tableHeaderView) and I cannot get it to work.
I have a simple label in the view that I want to cover the entire view. I have added the Trailing, Leading, Top, and Bottom constraints to the label against the parent's corresponding edges. When I do this, systemLayoutSizeFittingSize computes the correct height, but I get the following error:
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:0x7fbb71d9a310 V:|-(5)-[UILabel:0x7fbb71df5120'Test Label'] (Names: '|':mailapp.MailTableHeaderView:0x7fbb71d2c490 )>",
"<NSLayoutConstraint:0x7fbb71d2ebf0 UILabel:0x7fbb71df5120'Test Label'.bottom == app.HeaderView:0x7fbb71d2c490.bottom - 5>",
"<NSLayoutConstraint:0x7fbb71de31d0 V:[app.HeaderView:0x7fbb71d2c490(0)]>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x7fbb71d2ebf0 UILabel:0x7fbb71df5120'Test Label'.bottom == app.HeaderView:0x7fbb71d2c490.bottom - 5>
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.
Now, if I remove the "bottom" constraint on the label, auto layout works correctly but now systemLayoutSizeFittingSize returns 0.
What is the right way to make the constraints satisfy the auto layout engine but also to have systemLayoutSizeFittingSize compute the correct size?
As I mentioned, the most baffling thing is that I have used this successfully for complex auto layouts, but I noticed all those were UITableViewCells. I'm not sure if there is a difference.
I figured out the problem, but I don't understand why. For the UITableViewHeader, I cannot do the typical auto layout thing for the view. That is, I have to set translatesAutoresizingMaskIntoConstraints to be true. Effectively, I cannot use auto layout for the header view, and I have to set the frame manually.
Within the header view, I can use auto layout for the sub views successfully.
So my algorithm goes like this for constructing the tableHeaderView:
Create the header view with any arbitrary frame
Compute the constraints (of the header view's subviews)
Set the frame.width of the header view to match the parent tableView.frame.width
Layout the header view
Use systemLayoutSizeFittingSize to compute the height
Update the header view's frame.height
This works for me. I was not able to get it to work if I created the header view with translatesAutoresizingMaskIntoConstraints set to false.
As per the Apple Visual Format Language the following appears to be setting a '0' height constraint on your HeaderView.
E.g.
"<NSLayoutConstraint:0x7fbb71de31d0 V:[app.HeaderView:0x7fbb71d2c490(0)]>"
I would start by investigating this, as having a height of 0 will likely mess up your other label constraints for top and bottom spacing.

AutoLayout constraint issue with unexpected NSAutoresizingMaskLayoutConstraint

I'm using auto layout constraints programmatically and I am constantly seeing the same kind of error across my application usually related to a constraint that looks like this:
"<NSAutoresizingMaskLayoutConstraint:0x82da910 h=--& v=--& V:[UITableViewCellContentView:0x82d9fb0(99)]>"
I've put some sample code to reproduce at https://github.com/nicolasccit/AutoLayoutCellWarning
In this example, I am creating a very simple view with 2 UI elements: an image view called imageThumbnail and a label called labelName with some constraints:
"H:|-padding-[_imageThumbnail(==imageWidth)]-[_labelName]";
"V:|-padding-[_imageThumbnail(==imageHeight)]-padding-|";
"V:|-padding-[_labelName]";
On both elements I set the AutoresizingMaskIntoConstraints to NO.
And I am getting the following exception:
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:0xa6e4f90 V:[UIImageView:0xa6e4340]-(10)-| (Names: '|':UITableViewCellContentView:0xa6e4150 )>",
"<NSLayoutConstraint:0xa6e4f10 V:[UIImageView:0xa6e4340(80)]>",
"<NSLayoutConstraint:0xa6e4ed0 V:|-(10)-[UIImageView:0xa6e4340] (Names: '|':UITableViewCellContentView:0xa6e4150 )>",
"<NSAutoresizingMaskLayoutConstraint:0xa6e4ac0 h=--& v=--& V:[UITableViewCellContentView:0xa6e4150(99)]>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0xa6e4f90 V:[UIImageView:0xa6e4340]-(10)-| (Names: '|':UITableViewCellContentView:0xa6e4150 )>
I know the last constraint is related to the content view but I am unclear to properly remove it (Setting
AutoresizingMaskIntoConstraints to NO on the contentView raises an error and in the SO link below, it messes up the entire layout):
<NSAutoresizingMaskLayoutConstraint:0xa6e4ac0 h=--& v=--& V:[UITableViewCellContentView:0xa6e4150(99)]>
I've seen the answers at: Auto layout constraints issue on iOS7 in UITableViewCell but none of them seem to be working for me here.
I believe that the constraints I define are valid and pretty straightforward but can't seem to figure out what's going on. And I'm seeing the exception being raised both in iOS 6.1 and iOS 7.
Any idea what I am doing wrong here?
Thanks,
Nicolas
You should read the exception description more thoroughly:
Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints
In short, this constraint you are seeing is due to some UIView having it's translatesAutoresizingMaskIntoConstraints set to YES. In this case I would suspect this is the content view of the cell, as hinted to by UITableViewCellContentView.
You can disable it by just setting the property to NO.
cell.contentView.translatesAutoresizingMaskIntoConstraints = NO
EDIT:
Now, keep in mind that this is a temporary fix, most likely you have some other logic error with your constraints, for example constraining something in the contentView of the cell to the cell itself. Or by seemingly forcing the contentView to be larger than the cell is (and therefore larger than its' automatic sizing is).
For example, is your cell tall enough? i.e is it tall enough so that the contentView is 100pt tall? Note that the contentView has to be that tall, which might not necessarily match the height of the cell.
I've put a corrected version of your code at https://github.com/mattneub/AutoLayoutCellWarning. Works perfectly. This line was the main cause of your trouble:
NSString *const kImageVertical = #"V:|-padding-[_imageThumbnail(==imageHeight)]-padding-|";
Change that to
NSString *const kImageVertical = #"V:|-padding-[_imageThumbnail]-padding-|";
And all will be well.
The main reason you were having trouble is that by assigning an absolute height to the image view, you were making it impossible to also assign a height to the cell. Floating point is not exact, so you need to allow some room for the cell to grow / shrink. If we take away the absolute height, the image view gets its height from its intrinsic content size, at a lower priority, so there is no conflict.
I have some other critiques of your code. In trying to do dynamic setting of the cell's height while using auto layout, you were giving layout and constraint update commands you should never be giving, and you are giving them at wrong times. It is possible to do dynamic row heights based on constraints, but the way you're doing it is not the way. All you have to do is call systemLayoutSizeFittingSize to find out the correct cell height. Also, there is absolutely no need to put your "hidden" cell into the interface. Don't do that; it just confuses things. One of the things you'll notice when you look at my version of the code is that it is much simpler than yours, because of those differences.
For a working method, see my example at https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/bk2ch08p424variableHeights/ch21p722variableHeights/RootViewController.m
And see the discussion of this issue in my book.
EDIT (May 2014): Unfortunately my answer above fails to point out one of the key causes of this problem, namely, that the cell separator has height (if it hasn't been set to None). Therefore if you assign the cell a height that doesn't take the separator height into account, the auto layout constraints, if absolute, cannot be resolved into that height. (See this answer, which really made the lightbulb come on inside my head.)

Project really has no decent constraints yet, why is it giving me this constraint warning every time?

Every time I run my project, I get this warning in the console:
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) (
"<_UIScrollViewAutomaticContentSizeConstraint:0x8cc6830 UITableView:0xb3f5c00.contentHeight{id: 112} == -7.000000>" )
Will attempt to recover by breaking constraint
<_UIScrollViewAutomaticContentSizeConstraint:0x8cc6830
UITableView:0xb3f5c00.contentHeight{id: 112} == -7.000000>
Break on objc_exception_throw to catch this in the debugger. The
methods in the UIConstraintBasedLayoutDebugging category on UIView
listed in may also be helpful.
It's from a UITableView, and I'm definitely not touching the UIScrollView directly. It only has a label in each cell with these constraints:
I Googled and Googled but couldn't find anything. Does anyone know why it's complaining about UIScrollView?
if you want to remove the warnings, the quickest way to do it is in the storyboard.
select the view controller that causes the warnings to be displayed and select: clear all constrains in yourViewControllersName.
another option will be to select: reset to suggested constrains in yourViewControllersName.
these action will either clear all constraints, or add what ever Xcode thinks are the most suitable constraints to the view controller.
resting to suggested constraints will sometimes (or is it always?) add constraints that will not fit your needs..

Resources