79

I have a scroll view and an image view behind it and I am populating it with nibs. I am using autolayout. I have a bottom space to superview and a top space to superview on both of the views. The image view does exactly what I want it to do. For iphone 5 it is where I want it. And for the other iphones, it stays above the bottom of the screen, so it resizes correctly. The scroll view looks right on the iphone 5, but on the other phones it doesn't get resized, so it scrolls down below the view of the app. I get these messages in the log:

 2012-11-21 10:42:38.576 LCHApp[12604:907] 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:0x1d8ea080 UIScrollView:0x1d8413b0.bottom == UIImageView:0x1d892110.bottom>",
"<NSAutoresizingMaskLayoutConstraint:0x1d8cca10 h=-&- v=-&- ScheduleViewNib:0x1d853630.height == UIScrollView:0x1d8413b0.height - 386>",
"<NSLayoutConstraint:0x1d8e5340 V:[UIImageView:0x1d892110]-(64)-|   (Names: '|':ScheduleView:0x1d8efc30 )>",
"<NSAutoresizingMaskLayoutConstraint:0x1d8cf520 h=--& v=--& V:[ScheduleView:0x1d8efc30(480)]>",
"<NSLayoutConstraint:0x1d8eaed0 V:|-(45)-[UIScrollView:0x1d8413b0]   (Names: '|':ScheduleView:0x1d8efc30 )>"


 Will attempt to recover by breaking constraint 
 <NSLayoutConstraint:0x1d8ea080 UIScrollView:0x1d8413b0.bottom ==      UIImageView:0x1d892110.bottom>

I already tried

[self setTranslatesAutoresizingMaskIntoConstraints:YES];

and

[self.myScrollView setTranslatesAutoresizingMaskIntoConstraints:YES];

From what I can see this just takes off all constraints from the views. And isn't what I want.

SirRupertIII
  • 12,324
  • 20
  • 72
  • 121
  • 2
    Apple's [Technical Note TN2154: UIScrollView And Autolayout](https://developer.apple.com/library/ios/technotes/tn2154/_index.html) covers this issue in great detail. – smileyborg Jan 16 '14 at 17:09

4 Answers4

178

The relationship between UIScrollView and auto layout is different from other aspects of auto layout. Basically, if simple auto layout were allowed to operate, nothing would scroll. For example, if a subview of the scroll view were pinned in the normal way by a constraint to 10 points from the top of the scroll view, it would be absolutely pinned there; it would never move, no matter how the scroll view were scrolled.

To solve this problem, a UIScrollView that uses autolayout operates in a completely new way. Therefore when you say "I am using autolayout" you must prepare for things to operate very differently from before. You must either use a single scroll view subview with translatesAutoresizingMaskIntoConstraints = YES, and an explicit content size, or else everything must have translatesAutoresizingMaskIntoConstraints = NO and the content size will be deduced implicitly based on the constraints of the subviews.

This is very well explained in https://developer.apple.com/library/content/releasenotes/General/RN-iOSSDK-6_0/index.html

Cœur
  • 37,241
  • 25
  • 195
  • 267
matt
  • 515,959
  • 87
  • 875
  • 1,141
  • 5
    You may be interested in this question: http://stackoverflow.com/questions/14307037/bug-in-uitableview-layout-after-orientation-change (not least because of the bounty!) I think this is a scrollview / autolayout issue but to my mind it reads like a bug - in this case the scroll view is a table view, and the content size is not adjusting properly on rotation. – jrturton Jan 20 '13 at 20:49
  • 3
    @matt I don't know if you've seen this behavior, but if you use only layout constraints to define a scroll view's content size, there is a bug: scroll the content offset away from (0, 0) move to another tab or modal view controller, come back and the subviews of the scroll view are all shifted by the content offset and you can't get back. I would be grateful if you could confirm this and dupe my radar: http://openradar.appspot.com/radar?id=2932404 – edelaney05 Apr 13 '13 at 20:12
  • 3
    @edelaney05 I think I can confirm the bug, but before submitting to Apple you should make a demo project, esp. since copy-and-paste code in http://openradar.appspot.com/radar?id=2932404 won't even compile (you've typed the name of `constraintsWithVisualFormat:options:metrics:views:` wrong). Always use real code that actually works. And besides, in a real project you can include an actual image. The Apple people have no imagination and no time; you must do all the work for them in advance. – matt Apr 14 '13 at 00:37
  • 3
    @edelaney05 "shoulder consectetur pancetta"???? :) OK, good. I actually did one much more closely based on their code, with an image view sized to a big picture. By giving the scroll view a red background color, and with logging, it was very clear that the scroll view thought everything was right (content size, content offset), but was drawing the image view in the wrong place, i.e. offset by the amount of the content offset from when we left the view. – matt Apr 14 '13 at 02:18
  • 2
    @matt http://baconipsum.com ! :-) Awesome. Exact same behavior I was seeing. Scroll view reported everything as "normal," but drawing was incorrect by a factor of the offset. Thank you for confirming that I'm not crazy (but, only with regard to this bug!). – edelaney05 Apr 14 '13 at 20:12
  • 1
    @edelaney05 And of course this is just the kind of bug I want to know about, so thanks for telling me. (Also thanks for the baconipsum ref!) – matt Apr 14 '13 at 21:13
  • One obvious workaround for this bug is to not use auto-layout but I am loathe to do that. Any other workarounds? It seems like the constraint application code assumes that the scroll offset will be zero. To combat that, I tried intercepting layoutSubviews and setting the contentOffset to 0,0 before the constraints were applied, but sadly this had no effect. – phatmann May 31 '13 at 06:18
  • This answer http://stackoverflow.com/a/13326309/201828 has half of the right idea: zero out the contentOffset in viewDidDisappear. But if you try to restore the offset in viewWillAppear, the workaround fails. You can restore the offset in viewDidAppear. but this looks bad and I do not recommend it. Some posters suggested restoring the offset in viewDidLayoutSubviews, but that is not getting called me after the modal view is dismissed. – phatmann May 31 '13 at 06:41
  • The radar link above shows as closed as a dup of a non-existent other radar :-(. One of the dupes has what solved it for me: "If we save the content offset to a property in viewWillAppear: then reset it to CGPointZero, then in viewDidLayoutSubviews put it back to the previous offset, the view behaves as expected." http://openradar.io/13942147 – Peter Jul 01 '14 at 18:03
  • @Peter Wow, what an insane dance. - My bug report to Apple (13648613) remains open... I have not tried it in iOS 8 yet. – matt Jul 01 '14 at 18:31
  • @matt-- I'm getting ready to try fixing the massive break introduced into my animation code by iOS 8 (framed-based, worked perfectly in iOS 7, no longer works since iOS 8). I'm now pretty freaked about how to proceed since finding this thread. My animation involves programmatically-created 'UIViews' in a 'UIScrollView', animated via 'animateWithDuration'. Have you gotten a resolution from Apple? – rattletrap99 Dec 12 '14 at 20:08
  • @rattletrap99 That doesn't sound relevant to this question. There certainly is new view animation behavior in iOS 8 and it can certainly cause breakage, but it has nothing to do with scroll view autolayout. The incorrect scroll view layout bug was fixed long ago (during iOS 8 beta process). If you have a new question, ask a new question! – matt Dec 12 '14 at 20:10
  • I asked because the whole process of switching from frame-based to constraint-based animation is new and daunting to me, and my research led me here. The discussion regarding scrollView seemed to intimate yet another potential complication (or top of the other new stuff I'm trying to get my head around). Anyway, thanks for the clarification! – rattletrap99 Dec 12 '14 at 20:35
  • @rattletrap99 But frame-based vs. constraint-based animation was just as much the case in iOS 7 and iOS 6. It is not new. Animation and autolayout have _always_ been enemies. It's a huge problem: autolayout was introduced without considering it sufficiently, and Apple has been sweeping the damage under the rug ever since. See the rant in my book: http://www.apeth.com/iOSBook/ch17.html#_animation_and_autolayout – matt Dec 12 '14 at 21:49
  • @rattletrap99 And see my related essay here: http://stackoverflow.com/questions/12943107/how-do-i-adjust-the-anchor-point-of-a-calayer-when-auto-layout-is-being-used/14105757#14105757 – matt Dec 12 '14 at 21:50
  • Many thanks, @matt. This will obviously take me some time to digest, but the first couple of lines promise a lot of help. In case you're interested, here is my original question on this topic. It contains two brief videos demonstrating the behavior of my code under iOS 7.1 vs 8.1. Drastic, and unnerving, since I was modestly proud of the animation I'd achieved under 7.1. Anyway, thanks again for your generosity in sharing your wisdom! – rattletrap99 Dec 12 '14 at 22:16
  • Sorry--[here's the link to that question] (http://stackoverflow.com/questions/27385681/uiview-animatewithduration-behaves-differently-in-ios-8) and the videos. – rattletrap99 Dec 12 '14 at 23:05
  • @rattletrap99 Aha. I think one big change here that accounts for the difference in behavior is that in iOS 8 setting a label's text triggers layout immediately, whereas in iOS 7 and before it did not. See my answer here: http://stackoverflow.com/a/26964376/341994 However, you were always playing fast and loose, doing a lot of risky stuff inside an animation block that should not have been done there. – matt Dec 12 '14 at 23:35
  • Thanks for looking, @matt! I'm a fledgling, and I appreciate you pointing out the extra stuff in the animation block. I clearly have a major rewrite in front of me. I'll check your link now. – rattletrap99 Dec 13 '14 at 02:15
  • Thanks, That iOS 6 Release notes explains it all. – Lucien Jan 16 '15 at 12:57
163

Very important when using auto-layout: you must pin the right and/or bottom of the last subview to the right and/or bottom of the scroll view. This is how the scroll view knows the content size. For example:

[scrollView addConstraint:[NSLayoutConstraint constraintWithItem:lastSubView
                                                 attribute:NSLayoutAttributeRight
                                                 relatedBy:NSLayoutRelationEqual
                                                    toItem:scrollView
                                                 attribute:NSLayoutAttributeRight
                                                multiplier:1.0
                                                  constant:0]];

My thanks to this site for providing the perfect example.

I lost hours because of this, and I hope to spare others my pain.

phatmann
  • 18,161
  • 7
  • 61
  • 51
  • 4
    This is perfect. I have been looking for a simple example like the one in the link. Thank you!!! – pgpb.padilla Apr 20 '13 at 04:05
  • While matt's answer is good, this one is much more practically helpful for solving the issues I have had with using it – dmur Oct 29 '13 at 22:16
  • 22
    @dmur "While matt's answer is good, this one is much more practically helpful" - Dude, this is _also_ my answer. He got the code/explanation from my book! Glad I was able to help twice. :))) – matt Mar 27 '14 at 17:29
  • Depending on how you lay out your scrollView and their subviews, you may need to pin your stuff to the left or top. In my case, I had to pin everything to the left. – ArtSabintsev Sep 08 '14 at 23:20
  • You can also add the constraint via the Interface Builder. Great answer! – Moisés Olmedo May 18 '15 at 18:59
0

To get UIScrollviews to work nicely with constraints, I use this approach answered here. In that answer, I tackle how to get a vertically scrolling scrollview working that also works with device rotation. You can tweak the approach to work with horizontally scrolling scrollviews too. For scrollviews that scroll in both directions, don't add the size matching width constraint trick. But do everything else the same.

Community
  • 1
  • 1
n8tr
  • 5,018
  • 2
  • 32
  • 33
-3

A couple of things.

  1. make sure autolayout is on (IB on the "File Inspector Tab")
  2. Make sure you are NOT making any changes that involve bounds, frame, etc. - this is all done by Auto constraints now
  3. Make sure you stay away from AutoResizingMask. This will compete with your new settings. If these are done right, you can now layout your button and it will work great. Here's how.

This error is stating that either your nib or an a control within that nib is NOT using auto layout.

JustLearningAgain
  • 2,227
  • 4
  • 34
  • 50
  • 1
    But UIScrollView is special, and your answer fails to take that fact into account. – matt Nov 25 '12 at 03:37