Insetting, offsetting, transformation, and more!

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!

**SPONSORED** Catch bugs as soon as they happen and know exactly why a crash occurred by integrating Instabug's SDK in one minute. You will automatically receive device data, network logs, and reproduction steps with every bug and crash report.

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

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 a speaker at Swift events around the world. If you're curious you can learn more here.

Link copied to your pasteboard.