UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

Seven useful methods from CGRect

Insetting, offsetting, transformation, and more!

Paul Hudson       @twostraws

The humble CGRect struct might seem like a primitive beast, but it has a number of useful methods that let you adjust rectangles i a variety of ways.

Here are two simple CGRect instances we can work with:

let rect1 = CGRect(x: 20, y: 20, width: 100, height: 100)
let rect2 = CGRect(x: 50, y: 50, width: 100, height: 100)    

You can see that they overlap each other, and CGRect has three methods that let us evaluate that overlap. The first is the simplest: intersects() returns true if two rectangles overlap. In our case, this would be true:

rect1.intersects(rect2)

If you want more details, the intersection() method tells you where the intersection lies. Both our rectangles are 100 points wide, but the second one starts at X:50 and Y:50 so that’s where our overlap will begin. So, this code will return a new CGRect containing X:50 Y:50 width:70 height:70:

rect1.intersection(rect2)

The third option is the contains() method, which will return true if one rectangle entirely contains another. So, this will return false for us because the second rectangle lies partly outside the first:

rect1.contains(rect2)

There are four useful methods for transforming rectangles, but by far the most useful are insetBy() and offsetBy(). The former is used for expanding or contracting rectangles, and the latter for adjusting their positions.

For example, this will produce a rectangle at X:40 Y:40 width:60 height:60:

rect1.insetBy(dx: 20, dy: 20)

You’re specifying how much to grow or shrink the rectangle horizontally (dx) and vertically (dy) – these numbers apply to all four edges, which is why a horizontal change of 20 took our width from 100 to 60.

You can also make rectangles larger, like this:

rect1.insetBy(dx: -20, dy: -20)

I find negative insets helpful if you’re drawing behind something and want to make sure you have a fixed amount of margin on all edges.

As for offsetBy(), this is simply designed to move rectangles by whatever X and Y amount you need. Internally it just modifies the origin of the rectangle while also making sure it has positive width and height values, but using offsetBy() helps make your intent clearer.

So, this will move our rectangle 10 points down and to the right:

rect1.offsetBy(dx: 10, dy: 10)

For more advanced transformations, you can apply any CGAffineTransform to a CGRect by calling its applying() method. You should experiment in a playground first, though – these transforms work in their own unique way.

As an example, take a look at this code:

let transform = CGAffineTransform(scaleX: 2, y: 2)
rect1.applying(transform)

Our rect1 constant had X:20 Y:20 width:100 height:100, so after scaling by 2x there are several possible outcomes. For example, you might think that X and Y stay the same, but width and height double, or you might think that width and height double, but X and Y go down by 50 so the rectangle stays centered.

However, what this method actually does is scale up all four coordinate components: X, Y, width, and height all double, meaning that the finished rectangle starts at X:40 Y:40 – it has been scaled relative to the top-left corner.

One last method that’s worth mentioning is divided(atDistance:), which lets you easily split a rectangle into two parts. This returns two rectangles: the slice up to the point you specified, and the remainder afterwards.

As an example, we could divide rect1 into two rectangles, with the first slice taking up the first 40% of rect1 and the remainder taking up everything else:

let (slice, remainder) = rect1.divided(atDistance: rect1.width * 0.4, from: .minXEdge)

All these methods are things you could yourself, usually in a dozen lines of code or less. But do you really want to be spending your time thinking about rectangle transformations when Apple can do all that work for you? Probably not. CGRect isn't a complex struct, but it's worth letting it work its magic where possible!

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

BUY OUR BOOKS
Buy Pro Swift Buy Pro SwiftUI Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let us know!

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.