Height of iOS8 Today Extension using Only Auto Layout Gives Broken Constraints
Asked Answered
S

3

43

Apple documentation suggests setting the height of Today Extensions using autolayout.

If a widget has additional content to display, you can rely on Auto Layout constraints to adjust the widget’s height as appropriate. If you don’t use Auto Layout, you can use the UIViewController property preferredContentSize to specify the widget’s new height.

However, every example and tutorial I have seen ends up using preferredContentSize.

All of my attempts to set the height via autolayout lead to warnings of broken constraints.

Setting Height Via Autolayout

I started with a fresh xcode template, and a fresh today extension template. The only thing I added to the TodayViewController.m was:

- (UIEdgeInsets)widgetMarginInsetsForProposedMarginInsets:(UIEdgeInsets)defaultMarginInsets {
    return UIEdgeInsetsMake(0, 0, 0, 0);
}

Note: I still get this problem if I just use the default margins.

I constrained the label height, centered the label in the container, and constrained the container height to be the same as the label height:

Constrained Height

This should lead to a label that fills the container at the specified height with no constraint conflicts. Instead I get a constraint conflict:

2014-09-28 10:27:39.254 TodayExtension[61090:2672196] 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:0x7f8b8b62c670 V:[UILabel:0x7f8b8b62d9b0'Hello World'(124)]>",
    "<NSLayoutConstraint:0x7f8b8b583020 UIView:0x7f8b8b62d6e0.height == UILabel:0x7f8b8b62d9b0'Hello World'.height>",
    "<NSLayoutConstraint:0x7f8b8b5888a0 'UIView-Encapsulated-Layout-Height' V:[UIView:0x7f8b8b62d6e0(667)]>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x7f8b8b62c670 V:[UILabel:0x7f8b8b62d9b0'Hello World'(124)]>

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.

The way that it breaks the constraints, it actually ends up looking like I want it to: Constrained Height View

However, in other projects, it decides to break other constraints and it doesn't look correct. Also, side note: if I try and change the priority of the height constraint it crashes Xcode. So that's fun.

Not Constraining Height

I had hoped that since constraining the height didn't work, maybe if I didn't constrain the container height to the subviews, that perhaps it would figure out the required height to house the subviews and set itself correctly.

I centered the subview and constrained its height:i.imgur.com/PwLmhj9.png

This just led to an extension that uses up the full height of the notification center, and my correctly sized view vertically centered:

i.imgur.com/kKXlocu.png

If I don't center, but instead fix the vertical space to the top layout guide, I get the same thing except that the subview is fixed to the top (but the container is still huge).

What Gives?

I know I could just use preferredContentSize, but then why would Apple say it can be set using Auto Layout constraints? What am I doing wrong?

The examples I have given are obviously contrived. I'm setting the height of the view, so why not just set the height of the container, right? Part of the point of this in an actual project would be to set the height of the widget based on the width using only autolayout.

Swirsky answered 28/9, 2014 at 18:5 Comment(1)
I'm also experiencing this problem, however it only 'break' the first time it's run. The following occasions the bottom element in the my view moves up by a few pixels and there are no warnings.Zaid
S
36

After some experimenting, and stumbling across "what is NSLayoutConstraint "UIView-Encapsulated-Layout-Height" and how should I go about forcing it to recalculate cleanly" I determined that you can circumvent this problem using either "Height" & "Equal Heights" OR "Height" and Top and Bottom "Vertical Space" (as suggested by Guilherme Sprint), and then decreasing the "Priority" of the height constraint to 999.

Height Constraint with Reduced Priority

This isn't the answer I was hoping for, but it does specify the height of the container via Auto Layout of the subview and avoids any warnings about broken constraints.

My entirely unscientific guess/assumption/conclusion is that iOS is correctly looking at the layout constraints to determine the height of the container view. It then adds a new constraint that is the same height as what it just calculated, but this now over-constrains the height. Reducing the priority on the original developer specified height constraint means the system generated constraint wins out and no warning is generated (would love to hear more about this from someone who actually knows).

Swirsky answered 8/10, 2014 at 9:44 Comment(4)
Hours of searching, I've found your answer - thanks :) But why we have such crazy things... But "Failed to inherit CoreMedia permissions from" error don't wanna go :D Any idea?Adulterine
Unfortunately this does not solve the issue on Notification Center widgets. You're right that the autolayout warnings go away, but with this solution you always get a widget with 667 height. You might as well not even define your height constraint since it will always be shadowed by the system's one.Cumming
Did you know of the bounty'ed question at #30317077. Maybe you can solve it.Lugar
This worked for me by setting a height priority of High (750). Seems to be an issue with today view height set to 0 when it's first loaded.Hornswoggle
E
5

You should define 5 constraints in total to make the Today Extension the size that you really want.

4 constraints will define the relation with the superview:

  • Top Space
  • Bottom Space
  • Leading Space
  • Trailing Space

By the way, you should hook this constraints to the layout margins, as Apple recommends for view, and specially Today Extensions.

The 5th constraint will be the Height for the subview. This constraint, together with the top and bottom spaces, will define the exact size of your section inside Today Tab.

Xcode Today Extension Constraints

Just explaining that, the height constraint will set a constant for the subview, and the top and bottom constraints will "glue" the superview to the subview edges.

Today Extension running

Excursive answered 2/10, 2014 at 19:4 Comment(4)
I've got the same problem. I have tried this solution, but it's still not working. It looks right the first time I run it. But if I close the notification center and reopen it again, the subview becomes misplaced. I can also see in the log that Xcode complains about conflicting constraints. I will give up now and go for a solution with preferredContentSize.Bilyeu
It is not working for me too. I have the same issue as Magnus. Setup preferredContentSize also not working :(Farriery
This gives the same results as when I attempted to constrain using both "Equal Heights" and setting "Height". It does draw correct. However, as mentioned above, I get a warning about a broken constraint: Unable to simultaneously satisfy constraints...Will attempt to recover by breaking constraint <NSLayoutConstraint:0x7fa4c3748440 V:[UIButton:0x7fa4c3747830'Button'(124)]>. I had hoped to understand why AutoLayout thinks there is another constraint that has to be broken. My concern is that in other projects it chooses the wrong constraint to break.Swirsky
@Bilyeu But, after I set the preferredContentSize, it still happens, the subview still becomes misplaced! Have you solved this?Schlesinger
P
0

The height of the label makes it impossible to satisfy at the same time these constraints:

  • Label minimum height
  • Top Space to Superview
  • Bottom Space to Superview

You can solve it by removing the Bottom Space to Superview constraint, and the autolayout system will assign bottom space the remaining space.

Predicable answered 28/4, 2015 at 10:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.