2

This question is related to another question I just posted on Stackoverflow:
Layout Constraint Conflicts in Default Today Widget

I added a Today Extension as a target to my app, removed the default "Hello World" label that's inside the widget's root view and added a plain UIView in its place. I gave the view a yellow color and pinned it to all edges of the root view - the same way the label was constrained. Then I added another constraint to the yellow view in order to give it a fixed height of 100px.

Widget Storyboard

When I launch the app (tested on simulator and device) that height constraint is simply ignored and the yellow view takes up the whole available space all the way down to the header of the next widget.

When I swipe the notification center up and pull it down again the view suddenly jumps (it seems like it's suddenly "seeing" its own height constraint) leaving a vertical blank space of 39px at the bottom of the widget:

First launch  Subsequent launches

I figured that the 39px margin at the bottom is the default bottom margin of a today widget as passed in by the defaultMarginInsets parameter in the widgetMarginInsetsForProposedMarginInsets(defaultMarginInsets: UIEdgeInsets) method and that I can fix this inconsistent behavior by overriding this method and providing my own margin insets:

func widgetMarginInsetsForProposedMarginInsets(defaultMarginInsets: UIEdgeInsets) -> UIEdgeInsets {
    var newInsets = defaultMarginInsets
    newInsets.bottom = 20
    return newInsets
}

However, I would really prefer to use system provided margins instead of fixed values. It seems to me like this is another iOS bug regarding the today widget. Is it? And if not: How can I fix this?

Community
  • 1
  • 1
Mischa
  • 15,816
  • 8
  • 59
  • 117

1 Answers1

1

Try avoiding the use of pins.

For positioning, rely on aligning your view to the superview's leading, trailing, top, or bottom edges.

For sizing, try setting your view to have equal heights or widths with the superview. And adjusting the multiplier as needed.

This solved auto layout inconsistencies I was experiencing in a Today Widget.

Updated w/ screenshot:

enter image description here

See above, I am using the align menu (not the pin menu). I select both the view I am trying to constrain, along with the all encasing superview, and tell the prior to share (or, align with) trailing and bottom edges.

I know this is not how Apple may demonstrate it, however it is a workaround that avoids the bugs that occur when using pins with Today Widgets.

Update #2 - And here is all the constraints (including height and width):

enter image description here

The bug must be related to the inferred sizing of a view that is entirely pinned, because when I set the height and width of my view to be relative to its superview (rather than having it be inferred), the bug does not occur.

vikzilla
  • 3,998
  • 6
  • 36
  • 57
  • Thanks for your answer! The thing is: If I add constraints to the superview's leading, trailing, top and bottom margins (which I actually call "pinning" - I haven't heard of any other "pins" in iOS?) it includes sizing already (for positioning only 2 constraints are needed). So adding equal width and equal height constraints will sort of overconstrain the system. Even if it worked I shouldn't have to do that because that's not how AutoLayout is documented and even Apple's own Today Widget template is constrained this way! – Mischa May 09 '16 at 19:04
  • @Mischa I don't think you understood what I meant by using aligning constraints instead of pins, so I updated my answer above. – vikzilla May 09 '16 at 19:14
  • Thanks again for your update. Now I do better understand what you mean. However, there is only one kind of constraint in code and whether you select two views in Interface Builder and "align" their trailing edges right or you select the inner view and "pin" its trailing edge to the superview's trailing edge it's the same constraint that's being generated. – Mischa May 09 '16 at 19:35
  • Conceptually yes, however when I used aligning instead of pinning and the bugs did not occur. All I can suggest is try adding constraints without using the pin menu at all; rather rely on aligning and the other relative constraint methods and see if the bugs go away. They did for me. – vikzilla May 09 '16 at 19:38
  • I believe it must be related to some other thing then. Maybe Interface Builder created a constraint to the view controller's bottom layout guide instead of its view's bottom edge when you used the pin menu. Because I just checked the storyboard file in a text editor and it's just as I said: Either way you create the constraint it's the same code that's being generated behind the scenes: ``. And there's no way to tell afterwards how you created the constraint. – Mischa May 09 '16 at 19:48
  • Thats a good point. It must be related to the sizing of my view then: previously I was just pinning my view to all sides of the superview so the size was inferred. However now I am just aligning the trailing and bottom (which based on your findings would be the same as pinning the trailing and bottom). But rather than pin the other 2 sides, I am setting the view's height and width to be relative to the superview; thus, the bug must occur when the size is inferred for a view that is entirely pinned, and the bug does not occur when giving the view a relative height and width (see updated answer) – vikzilla May 09 '16 at 19:53
  • I just tried exactly what you suggested, setting only a trailing and bottom constraint as well as an equal width and equal height constraint but for me the result was the same as before: The bug did occur as well. I see from your screenshot that you have a stackview included in your view hierarchy. From what I've experienced so far with the Today Widget it's very possible that some magic inside the stackview causes this particular setup to work. Unfortunately it doesn't seem to be a general solution. – Mischa May 09 '16 at 20:18