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 = CGRectZerolet size = CGSizeZerolet 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.zerolet size = CGSize.zerolet 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.widthCGFloat 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.
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.widthlet height = frame.heightlet maxX = frame.maxXlet 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 CGRect
s 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 view
’s 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 += 10UIOffsetvar 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.