Unexpected frame and bounds sizes

I've put a UIView subview into my storyboard scene and pinned its leading and trailing edges to the sides of the main view. I then added a center-vertically restraint to the subview, and than an apect-ratio-1:1 constraint. Just as I expected, the constraints make the subview into a square and place it in the center of the main view.


I have the subview connected to an IBOutlet in my view controller. But when I print the subview.bounds and subview.frame to the console, I get (0, 0, 240, 128), which I didn't expect. Furthering my confusion, this happens on both the iPhone 5 and iPhone 6 simulators, with have different widths.


Obviously there's something fundamental missing in my understanding of views. I'm reading the UIView programming guide end to end, to see if I can finally remedy this intellectual defect. But in the meantime, I'd be very grateful if someone would explain what's happening in this particular case.

At what point are you printing the frames? If it's in viewDidLoad, that's too early. Everything will still have whatever frame size and position it had on the storyboard. Layout has not yet been performed. Frames do not have valid values until after layout has been performed, i.e. viewDidLayoutSubviews or later.

I was doing it first in viewDidLoad, and then in viewWillAppear. After reading you comment (and kicking myself around the room a couple of times), I moved the code into viewDidLayoutSubviews. I now get this:


Frame: (16.0, 140.0, 288.0, 288.0)

Bounds: (0.0, 0.0, 288.0, 288.0)


Still not what I expected for width and height; but it made me feel better nonetheless. Where else might I put this code, so that it runs even later in the layout process?


Ultimately I want to put two UIImageViews into this UIView. They will each contain an image of an outline map of a country. The image files will all be the same size (500x500, perhaps), and each will be just large enough to contain the map outline. What I want to so is to resize one of these image views, so that the map outlines appear at the same scale within the UIView. (And of course have them centred nicely.) The idea is to show the user the relative sizes of the countries themselves.

This seems to do the trick:


@IBOutlet weak var mapView: UIView!

override func viewDidLayoutSubviews() {
  super.viewDidLayoutSubviews()

  let backgroundImage = UIImage(named: "brazil-red.png")
  let foregroundImage = UIImage(named: "argentina-yellow.png")

  let backgroundImageView = UIImageView(image: backgroundImage)
  let foregroundImageView = UIImageView(image: foregroundImage)

  self.mapView.addSubview(backgroundImageView)
  backgroundImageView.frame = mapView.bounds

  self.mapView.addSubview(foregroundImageView)
  foregroundImageView.frame = mapView.bounds

  let scaleFactor: CGFloat = 0.7
  foregroundImageView.transform = CGAffineTransformMakeScale(scaleFactor, scaleFactor)
}


The mapView is configured in IB as I described in my previous post: its leading and trailing edges are pinned to the sides of the main UIView, it's centred vertically, and its aspect ratio is 1:1.


Both png files are the same size. But now I get a scaled-down version of Argentina in the centre of the UIView, superimposed on Brazil. This is just what I wanted.

Couple of things to note - viewDidLayoutSubviews may be called many times during the lifetime of the view controller, so it is not an appropriate place to add subviews. To lay out existing subviews, sure, if you must do manual layout, but not to add new ones every time.


Personally I would add my subviews and appropriate auto layout constraints in viewDidLoad. Then an implementation of viewDidLayoutSubviews is not needed.

I tried adding my UIImageViews in viewDidLoad, and got distorted images when I set the frame to the superview bounds. I thought this was because the view had not been fully initialized yet. Where should I add my UIImageViews to their superview?


P.S. Things look fine when I add the image views in viewDidAppear. Is that the correct place then?

Personally, I add subviews and auto layout constraints in viewDidLoad. I never set frames.


If you must do manual layout for some reason (and there are very few good reasons nowadays, IMHO), add your subviews in viewDidLoad, and set their frames in viewDidLayoutSubviews.

If I may impose upon you for yet more advice... How would I do this:


self.mapView.addSubview(foregroundImageView) 
foregroundImageView.frame = mapView.bounds


in code using layout constraints?

I would suggest Googling "auto layout constraints swift" for lots of examples. You could use constraintsWithVisualFormat with strings like "H:|[myView]|", or you could add the four constraints (top, bottom, leading, trailing) individually using constraintWithItem: or the new fangled "anchor" methods.


In your case though, you could easily add all the constraints on the storyboard so actually there is no need for any code at all.

Unexpected frame and bounds sizes
 
 
Q