Bharat Modi's answer pretty much solves the issue, but I (Xcode 8.3.2, iOS 10 SDK) also had to match the Content View's height and width to UIScrollView's height and width.
This will pin the content size to just visible area, but it makes IB happy. Then (I think) it's just a matter of calculating the content size correctly and setting it programmatically. Which I would've had to do anyway, since if you have dynamic content you can only do that at run time.
You might also need to tweak the "Adjust scroll view insets" settings of the view controller if you have a navigation bar.
This is what I have:
Note that this could be just a peculiarity of my setup, because the official suggestions (https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/WorkingwithScrollViews.html) also don't mention that this needs to be done.
I tried figuring this out using just a scroll view, a content view and another dummy view of fixed width and height to imitate the content, but nothing seemed to make IB happy.
(Sorry, I don't have enough rep to put this as a comment on the original answer).
UPDATE:
So actually what I ended up doing is setting my Content View to a fixed height in IB and creating an outlet for that height constraint in the code. Then after the view has loaded all of its dynamic content I just update the height constraint like so:
self.uiContentViewHeightConstraint.constant = mapViewBottomY + 15
And everything works. The key here is basically that when there's a view inside a scroll view - that view defines scroll view's content size. As people have mentioned before, size of content view has to be fully defined at build time in order for IB to be happy. You are free to manipulate the values at run time though through outlets - it's easy and safe.
One advantage of having the content view at fixed size in IB is that since you've fixed the content size, views inside the content view can now be defined as usual, using relative offsets.