VAPOR 3: Learn Server-Side Swift with hands-on projects >>

Seven useful methods from CGRect

Paul Hudson    May 17th 2018    @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!

 

MASTER SWIFT NOW
Buy Pro Swift Buy Swift Design Patterns Buy Practical iOS 11 Buy Swift Coding Challenges Buy Server-Side Swift (Vapor Edition) Buy Server-Side Swift (Kitura Edition) Buy Hacking with macOS Buy Advanced iOS Volume One Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with Swift Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

About the author

Paul Hudson is the creator of Hacking with Swift, the most comprehensive series of Swift books in the world. He's also the editor of Swift Developer News, the maintainer of the Swift Knowledge Base, and Mario Kart world champion. OK, so that last part isn't true. If you're curious you can learn more here.

Was this page useful? Let me know!

Click here to visit the Hacking with Swift store >>