Position, Move and Stretch a UIView with contentMode
Predict what will happen when bounds change
I drew a circle using a
UIBezierPath in a custom
UIView. No problem — except on rotation the circle turned into an oval — less cool.
The solution involved no code at all. You simply set the contentMode to .redraw and.. Solved! Solution from the Storyboard:
But wait. There are 13 different options here. What are they, and what do they do? That’s what this article is about!
- Be able to create a Single View Application, and this tutorial expects that you are comfortable with Subclassing a UIView, and
The example project
We can place a man in an UIImageView using AutoLayout constraints. You’ll also need to have an image called man nestled inside your Asset catalog.
Something like the following:
Now within the
viewDidLoad() function of your view controller you can place some code to initialize the UIImageView and the image.
Which will present the man on the screen (when the App is run)
Which is…not ideal. So what’s going on?
The answer is in the commented out piece of code in the view controller.
// programaticView.contentMode = .scaleToFill
contentMode is actually the default (so it doesn’t matter if we uncomment this line or not in practice.
.scaleToFill stretches the view to fill the available space without maintaining the aspect ratio, and this means that our image looks awful.
Because the image in question has been set to the UIImageView with the height and width anchors as described above are larger than the image that we have elected to use (the man is 128x128).
There are some situations where you’d want this — perhaps you always want the image to be a certain size whether that means stretching it or not.
Scaling the view
For each of these screen shots I’ve changed the orientation of the simulator, simply to make this easier to see the image. With that said, let’s go:
We have already seen the default,
.scaleToFill does not respect the aspect ratio.
This scales the image to fit the view, maintaining the aspect ratio but no section of the image is cropped. This means that there are elements of the view the remain transparent.
Maintain the aspect ratio of the content, and some of the image can be clipped.
But wait — we can see that this image hasn’t been clipped. It isn’t repecting the bounds of the image. We can make it do so by setting
.clipsToBounds to be true, which would be
programaticView.clipsToBounds = true in the view controller above.
Positioning the view
We can position the view by making the
.contentView be either
.bottomLeft. These act pretty much as how you might expect.
We can see that the image of the man is not stretched.
This is back where the article began. The redraw method redraws the view when the bounds are changed (this happens when
setNeedsDisplay() is invoked, and on orientation change).
This seems to be the same as
.scaleToFill on the surface. But that brings us back to the initial part of the problem — drawing your own image.
The solution here would be
.redraw which is back to the beginning of the article.
It might do you well to recognize that these options are Enumeration Cases rather than simple properties, although in practice (and the examples shown above) there is little difference. Apple have covered contentMode (without the example) which would show you the differences — without the examples shown above.
I hope this article has been of help to you, and if you have any questions or queries I’d love hear from you!