2

I'm doing my first iOS app, and I'm a little bewildered by the way sizing and layout works (or, in my case, doesn't). I feel like what I'm trying to do is (or should be) very simple, but everything I try isn't having the desired result.

In my initial view controller, I have a UIScrollView:

enter image description here

Programmatically, I'm adding a subview that's loaded from a XIB using .addSubView:

var frame = scrollView.bounds
frame.origin.x = 0.0
frame.origin.y = 0.0

let view = pages[page]                // pages[page] is UIView loaded from XIB
view.contentMode = .ScaleAspectFit
view.frame = frame
scrollView.addSubview(view)

So now on to the XIB (I changed the view to "Freeform" size from "Inferred" only for the purposes of taking this screenshot):

enter image description here

There are labels in three of the corners, and what I would like is for them to be aligned so that they always stay in those corners, and the centered text stays centered. When I run it, though, things are positioned in their same old boring places (I added a background so you can see the problem):

enter image description here

What I've tried (unsuccessfully):

  • Using constraints to position the elements (doesn't seem to do anything, leading me to believe the problem is with the sizing of the child view
  • Setting various size and layout properties of the child view

I am under a tight deadline, so my preference would be a way to do this that isn't too involved, but eventually I would like to have solid fundamentals and really know what's going on here. My question is multipart (but related):

  • Given that I'm inexperienced, are constraints the best way to position the view elements so that they align left, right, center, etc.?
  • What do I need to do to get the child views to size correctly in the UIScrollView?
  • Do I need to do anything special to re-layout if screen orientation changes?

Any advice would be helpful: specific techniques, tutorials, appropriate documentation (I've been reading the documentation, but it's a big forest, and I'm feeling a little lost).

This is a chance for one of you iOS wizards to shine with a great answer!

Ethan Brown
  • 26,892
  • 4
  • 80
  • 92
  • 1. Yes, constraints are the best way to position and size your subviews. 2. You should also use constraints to size your child views. 3. If you still want your labels in the corners and centered in the middle like your image, then no, you don't need to do anything on rotation. The scroll view itself should also have constraints to its superview. Can you explain in a little more detail what you're trying to accomplish? Do you have multiple child views you want to add? Are they bigger than the scroll view in one dimension only (so only horizontal or vertical scrolling)? – rdelmar Dec 18 '14 at 04:35

2 Answers2

1

I think the answer to this question might help you - iOS Autolayout with UIScrollview: Why does content view of scroll view not fill the scroll view?

"The constraints between of contentView and its superview (the scrollView) are to the scrollView's contentSize, not to its frame"

Community
  • 1
  • 1
1

Here is some example code. I added a scroll view in the storyboard and added constraints to size and position it (pinned to left and right sides, vertically centered, and a fixed height of 200). I created two xib files with views, each of which had 4 corner labels an one in the center. The center one has centerX and centerY constraints, and the corner ones each have constraints to the nearest two edges. I made these views 200 points high in the xib, just so I could see what the layout would look like when I added them to the scroll view, but it's not necessary to do this; the constraints I will be adding will take care of sizing the views appropriately. Here is the code I have in the controller.

class ViewController: UIViewController {

    @IBOutlet weak var scrollView: UIScrollView!
    override func viewDidLoad() {
        super.viewDidLoad()

        let firstSubview = NSBundle.mainBundle().loadNibNamed("View", owner: self, options: nil).first as UIView
        firstSubview.setTranslatesAutoresizingMaskIntoConstraints(false)
        scrollView.addSubview(firstSubview)

        let secondSubview = NSBundle.mainBundle().loadNibNamed("View2", owner: self, options: nil).first as UIView
        secondSubview.setTranslatesAutoresizingMaskIntoConstraints(false)
        scrollView.addSubview(secondSubview)
        scrollView.pagingEnabled = true

        let scrollViewHeight = scrollView.frame.size.height

        let viewsDict = ["first": firstSubview, "second": secondSubview]

        scrollView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("|[first(vw)][second(vw)]|", options: nil, metrics: ["vw":scrollView.frame.size.width], views: viewsDict))
        scrollView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[first(svh)]|", options: nil, metrics: ["svh": scrollViewHeight], views: viewsDict))
        scrollView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[second(svh)]|", options: nil, metrics: ["svh": scrollViewHeight], views: viewsDict))


    }

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        println("scroll view size is: \(scrollView.frame.size)")
        println("scroll view contentSize is: \(scrollView.contentSize)")
    }

}

You'll notice that the format string for the first (horizontal) constraint would cause an error if you were making it to a normal view: "|[first(vw)][second(vw)]|" Here, vw is defined in the metrics as the scrollView.frame.size.width, so it appears that I'm adding two views that are the width of the scroll view, but pinning them to the sides. This works in a scroll view because those sides (represented by the "|" character) are to the contentView not the scroll view itself. So, these constraints cause the contentView to be twice as wide as the scroll view (so you don't need to set that explicitly yourself). The log in viewDidAppear gives me this,

scroll view size is: (320.0,200.0)
scroll view contentSize is: (664.0,200.0)
rdelmar
  • 103,982
  • 12
  • 207
  • 218
  • Hmm...I learned a lot from this, thank you! However, I don't think it's quite what I'm looking for.... In particular, I don't want to to have to add constraints for each view _to the scroll view_. That is, I want to be able to say "every subview of the scroll view should take up its entire height, and a specified width". Then each subview lays itself out according to its constraints.... – Ethan Brown Dec 18 '14 at 23:17