Swift: CGRect, CGSize & CGPoint

You’re (probably) doing it wrong

It’s been about a solid eight months since I first decided to jump completely into the Swift wormhole. Over those months I’ve slowly learned to stop writing Objective-C style code patterns with Swift syntax, and started to actually take advantage of the new language.

But one thing I’ve recently picked up on is that I’m still using ugly non-Swift-like syntax with all my CGGeometry structures.

CGRect, CGSize, CGPoint

C syntax. A sheep in wolf’s clothing

I have a big feeling that a lot of Swift developers are going to be guilty of this. Raise your hand if you have been using either of these in your code:

let rect = CGRectMake(0, 0, 100, 100)
let point = CGPointMake(0, 0)
let size = CGSizeMake(100, 100)
Update: As of Swift 3, these functions have been removed.

Yea, I thought so. But don’t worry, it’s nothing to be ashamed of …yet.

The thing that’s wrong with this is that it breaks Swift’s coding convention, it will still work, but it looks a lot more like something that if it belongs in Objective-C or even, dare I say it, Java. *shudders*

To a seasoned iOS or OSX developer, with a short glance they could easily tell you what that code is doing. They don’t need argument labels on CGGeometry structures because everyone knows those arguments, and their sequence off by heart. Except they don’t.

A lot of what Swift is about is being friendly to language newcomers as well as complete programming beginners. If they were to look at that code, they would have no idea what those numbers represent. So let’s all vow to be good Swift citizens and start using the Swift versions:

let rect = CGRect(x: 0, y: 0, width: 100, height: 100)
let size = CGSize(width: 100, height: 100)
let point = CGPoint(x: 0, y: 0)

For a little bit extra syntax, the verbosity of our code is completely understandable at first glance. Also, an added bonus of using the CGGeometry struct initializer is that now we aren’t just confined to using CGFloat as arguments, we can also pass in Int and Double!

Zero

let rect = CGRectZero
let size = CGSizeZero
let point = CGPointZero
Update: As of Swift 3, these functions have been removed.

While we’re at it, you’re probably still using these too, amirite? lel.

We should also be updating these to use the newer, Swiftier syntax. But don’t worry, the syntax is only just an extra character longer. Can you guess what it is?

let rect = CGRect.zero
let size = CGSize.zero
let point = CGPoint.zero

Another thing to get you on board with this is that Xcode’s syntax highlighter will, depending on your color scheme, highlight the .zero, making it that little bit lighter on your cognitive load.

Value retrieval

CGRect frame = CGRectMake(0, 0, 100, 100)
CGFloat width = CGRectGetWidth(frame)
CGFloat height = CGRectGetHeight(frame)
CGFloat maxX = CGRectGetMaxX(frame)
CGFloat maxY = CGRectGetMaxY(frame)
Update: As of Swift 3, these functions have been removed.

If you were or still are a good Objective-C citizen, you would have used these value getters when you wanted a specific value of the rect. But wait a second, why don’t we just access the values directly?

CGFloat width = frame.size.width
CGFloat height = frame.size.height
For this reason, your applications should avoid directly reading and writing the data stored in the CGRect data structure. Instead, use the functions described here to manipulate rectangles and to retrieve their characteristics.
Apple, CGGeometry Reference Documentation

There’s probably still a lot of you that didn’t bother with such formalities, but that’s ok. Swift has alleviated us of such an unpretty API and instead provided simple dot notation variables.

let frame = CGRect(x: 0, y: 0, width: 100, height: 100)
let width = frame.width
let height = frame.height
let maxX = frame.maxX
let maxY = frame.maxY

There’s plenty more examples of these niceties available in the Swift playgrounds I’ve provided to supplement this post. The link will be at the bottom of the page.

Mutability

let frame = CGRect(x: 0, y: 0, width: 100, height: 100)
let view = UIView(frame: frame)
view.frame.origin.x += 10

Not only are you able to change individual values of a view’s frame, but you’re also able to replace entire sub-structs completely. Behold:

let view = UIView(frame: .zero)
view.frame.size = CGSize(width: 10, height: 10)
view.frame.origin = CGPoint(x: 10, y: 10)

This little feature alone is enough of a reason to stop using Objective-C. Mutability on CGRects without having to create a new one entirely and reset the value. It was only less than two years ago that we as Objective-C developers were forced to write frame modification code like this:

CGRect frame = CGRectMake(0, 0, 100, 100);
UIView *view = [[UIView alloc] initWithFrame: frame];
CGRect newFrame = view.frame;
newFrame.size.width = view.frame.origin.x + 10;
view.frame = newFrame;

I don’t know about you, but writing that really stressed me out. Creating a newFrame struct from the views frame, modifying it, and then resetting the frame property on the view again. No thank you, not ever, ever again please.

Don’t forget

There are also a couple of other constructs within UIKit for which these changes are applicable to:

UIEdgeInsets 
var edgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
edgeInsets.top += 10
UIOffset
var offset = UIOffset(horizontal: 10, vertical: 10)
offset.vertical += 10

Sample code of this post can be found on GitHub.


If you like what you’ve read today you can check our my other articles or want to get in touch, please send me a tweet or follow me on Twitter, it really makes my day. I also organise Playgrounds Conference in Melbourne, Australia and would to see you at the next event.