0

I'm trying to convert part 1 of the Ray Wenderlich UIScrollView Tutorial into Autolayout with universal devices. I currently have a UITableView embedded in a Navigation Controller, connecting a UIViewController of class ViewController with a UIScrollView (of no custom class). The Navigation Controller has an opaque navigation bar and no toolbar. The Attributes for the UIViewController can be seen in the first image in the album below.

Image Album

With just the four storyboard constraints (outlined below), my view looked like the second image in the album, with the scrollView's frame size at an incorrect 600x536. To fix this, I decided to add the constraints again at runtime, in viewDidLoad() in ViewController.swift. I now have 8 total constraints:

  • 4 constraints on the storyboard:
    • scrollView.top = topLayoutGuide.bottom
    • scrollView.bottom = bottomLayoutGuide.top
    • scrollView.leading = superView.leading
    • scrollView.trailing = superView.trailing
  • 4 contraints in ViewController.swift:
    • scrollView.top = topLayoutGuide
    • scrollView.bottom = view.bottom
    • scrollView.leading = view.leading
    • scrollView.trailing = view.trailing

With these constraints, before zooming, my view now looks like the third album image, with scrollView's frame size at a still incorrect 375x667. After zooming, my view is correct, looking like the fourth album image, with scrollView's frame size at a correct 375x603. A few other notes:

  • self.topLayoutGuide.length = 0. Shouldn't it be 64? 20 from the status bar and 44 from the navigation bar?
  • Removing the storyboard constraints leads to the same problem as not having programmatic constraints. Why do the storyboard constraints still need to be there?
  • I have tried both pure and mixed approaches, neither of which have solved this problem.
  • As well as disabling Adjust Scroll View Insets, Extend Edges Under Top Bars, and Extend Edges Under Opaque Bars in the storyboard (see first image), I am also disabling them in code, to no avail.
  • I can't use insets because the frame of the UIScrollView changes after zooming.

How can I get the UIScrollView to be a constant, proper size?

Thank you.

class ViewController: UIViewController, UIScrollViewDelegate

@IBOutlet var scrollView: UIScrollView!

var imageView: UIImageView!

override func viewDidLoad() {
    super.viewDidLoad()

    //Reset the layout
    scrollView.needsUpdateConstraints()
    scrollView.setNeedsLayout()
    scrollView.layoutIfNeeded()

    //Set storyboard settings just in case
    scrollView.setTranslatesAutoresizingMaskIntoConstraints(false)
    self.extendedLayoutIncludesOpaqueBars = false
    self.edgesForExtendedLayout = .None
    self.automaticallyAdjustsScrollViewInsets = false
    self.navigationController?.navigationBar.translucent = false

    //Manually added constraints at runtime
    var constraintTS = NSLayoutConstraint(item: scrollView, attribute: .Top, relatedBy: .Equal, toItem: topLayoutGuide, attribute: .Bottom, multiplier: 1.0, constant: 0)
    var constraintBS = NSLayoutConstraint(item: scrollView, attribute: .Bottom, relatedBy: .Equal, toItem: view, attribute: .Bottom, multiplier: 1.0, constant: 0)
    var constraintLS = NSLayoutConstraint(item: scrollView, attribute: .Leading, relatedBy: .Equal, toItem: view, attribute: .Leading, multiplier: 1.0, constant: 0)
    var constraintRS = NSLayoutConstraint(item: scrollView, attribute: .Trailing, relatedBy: .Equal, toItem: view, attribute: .Trailing, multiplier: 1.0, constant: 0)
    view.addConstraint(constraintTS)
    view.addConstraint(constraintBS)
    view.addConstraint(constraintLS)
    view.addConstraint(constraintRS)

    println(scrollView.frame.width)
    println(scrollView.frame.height)

    //Original Ray Wenderlich code
    let image1 = UIImage(named: "photo1")!
    imageView = UIImageView(image: image1)
    imageView.frame = CGRect(origin: CGPoint(x: 0, y: 0), size: image1.size)
    scrollView.addSubview(imageView)
    scrollView.contentSize = image1.size

    let scrollViewFrame = scrollView.frame
    let scaleWidth = scrollViewFrame.width / scrollView.contentSize.width
    let scaleHeight = scrollViewFrame.height / scrollView.contentSize.height
    let minScale = min(scaleWidth, scaleHeight)
    scrollView.minimumZoomScale = minScale
    scrollView.maximumZoomScale = 1.0
    scrollView.zoomScale = minScale

    centerScrollViewContents()
}

func centerScrollViewContents() {
    let boundsSize = scrollView.bounds.size
    var contentsFrame =  imageView.frame

    if contentsFrame.size.width < boundsSize.width {
        contentsFrame.origin.x = (boundsSize.width - contentsFrame.width) / 2.0
    } else {
        contentsFrame.origin.x = 0.0
    }

    if contentsFrame.size.height < boundsSize.height {
        contentsFrame.origin.y = (boundsSize.height - contentsFrame.height) / 2.0
        println("boundsSize.height: \(boundsSize.height)")
        println("boundsSize.width:  \(boundsSize.width)")
        println("contentsFrame.origin.y: \(contentsFrame.origin.y)")
    } else {
        contentsFrame.origin.y = 0.0
    }

    imageView.frame = contentsFrame
}

func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
    return imageView
}

func scrollViewDidZoom(scrollView: UIScrollView) {
    centerScrollViewContents()
}

override func viewWillLayoutSubviews() {
    println(self.topLayoutGuide.length)
}
natec
  • 254
  • 1
  • 3
  • 16

1 Answers1

0

Solved! Thanks to Working with Top Layout Guide in UIScrollView through Auto Layout. I didn't think a UIScrollView would make topLayoutGuide behave differently, but apparently it does. Also turns out I didn't need to set the constraints; scrollView.layoutIfNeeded() does the trick! My scrollView is now a constant 375x667, removing the resizing issue and giving self.topLayoutGuide.length = 64. This allows me to center the view vertically by subtracting self.topLayoutGuide.length in centerScrollViewContents(). I also call centerScrollViewContents() in viewWillLayoutSubviews(). Here's my new code:

@IBOutlet var scrollView: UIScrollView!

var imageView: UIImageView!

override func viewDidLoad() {
    super.viewDidLoad()

    //Redo constraints at runtime
    scrollView.layoutIfNeeded()

    //Original Ray Wenderlich code
    let image1 = UIImage(named: "photo1")!
    imageView = UIImageView(image: image1)
    imageView.frame = CGRect(origin: CGPoint(x: 0, y: 0), size: image1.size)
    scrollView.addSubview(imageView)
    scrollView.contentSize = image1.size

    let scrollViewFrame = scrollView.frame
    let scaleWidth = scrollViewFrame.width / scrollView.contentSize.width
    let scaleHeight = scrollViewFrame.height / scrollView.contentSize.height
    let minScale = min(scaleWidth, scaleHeight)
    scrollView.minimumZoomScale = minScale
    scrollView.maximumZoomScale = 1.0
    scrollView.zoomScale = minScale
}
func centerScrollViewContents() {
    let boundsSize = scrollView.bounds.size
    var contentsFrame =  imageView.frame

    if contentsFrame.size.width < boundsSize.width {
        contentsFrame.origin.x = (boundsSize.width - contentsFrame.width) / 2.0
    } else {
        contentsFrame.origin.x = 0.0
    }

    if contentsFrame.size.height < boundsSize.height {
        contentsFrame.origin.y = (boundsSize.height - contentsFrame.height - self.topLayoutGuide.length) / 2.0
    } else {
        contentsFrame.origin.y = 0.0
    }

    imageView.frame = contentsFrame
}

override func viewWillLayoutSubviews() {
    println(self.topLayoutGuide.length)
    centerScrollViewContents()
}

All other functions have not been changed.

Community
  • 1
  • 1
natec
  • 254
  • 1
  • 3
  • 16