The solutions above work, but I tried to understand why. I would like to share these two articles, which I found extremely useful when setting up mininumZoomScale correctly and using this to work with iOS8 / Xcode 6.
First, in order to get a scroll view for working in iOS8, you need to set the correct limits from the start. Other posters recommended, and I agree that there should only be one view of the content in your scroll view. It’s great to embed all of your other views into one content view, but you don’t want to set scroll view restrictions on a bunch of small views.
Combine the top, leading, trailing, and lower limits with the scroll view from the content view (in my case, it is a UIImageView) with a constant of 0. This sets the edges of your scroll view of the content view.
Then set the size of the content view using restrictions of equal width and equal height for the scroll view. You cannot use fixed width and fixed height in the content view, as scaling will not work, and you will not be able to control the size yourself. Without these restrictions, iOS8 sets the size to (0,0) before you get a chance leading to some odd artifacts. With these restrictions, call the updateZoom method in the orientation method, and you are kind. In iOS7, call it in didRotateFromInterfaceOrientation. In iOS8, call it from viewWillTransitionToSize. You will need to update the code for the new size.
Many thanks to Natasha Robot for solving the restriction: http://natashatherobot.com/ios-autolayout-scrollview/
When you call your updateZoom method to set your minimum, you should also check if the current current zoom is <your minimumZoomScale and reset, if so. You should not reset zoomScale otherwise, as this will become another suspicious look of your user when changing orientation.
As for the rest and why the math works, Joe Conway in objc.io put it all in this fantastic border-frame article using UIScrollViews: http://www.objc.io/issue-3/scroll-view.html
Thanks to both and the poster above.
-(void)updateZoom { self.scrollView.minimumZoomScale = MIN(self.scrollView.bounds.size.width / self.imageView.image.size.width, self.scrollView.bounds.size.height / self.imageView.image.size.height); if (self.scrollView.zoomScale < self.scrollView.minimumZoomScale) self.scrollView.zoomScale = self.scrollView.minimumZoomScale; }