Advertisement

iOS Fundamentals: Frames, Bounds, and CGGeometry

by
Student iconAre you a student? Get a yearly Tuts+ subscription for $45 →

Working with CGPoint, CGSize, and CGRect structures isn't difficult if you're used to a language that supports the dot syntax. However, programmatically positioning views or writing drawing code is verbose and can become difficult to read.

In this tutorial, I'd like to clear out a few misconceptions about frames and bounds, and introduce you to CGGeometry, a collection of structures, constants, and functions that make working with CGPoint, CGSize, and CGRect that much easier.

1. Data Types

If you're new to iOS or OS X development, you may be wondering what CGPoint, CGSize, and CGRect structures are. The CGGeometry Reference defines a number of geometric primitives or structures and the ones we are interested in are CGPoint, CGSize, and CGRect.

As most of you probably know, CGPoint is a C structure that defines a point in a coordinate system. The origin of this coordinate system is at the top left on iOS and at the bottom left on OS X. In other words, the orientation of its vertical axis differs on iOS and OS X.

CGSize is another simple C structure that defines a width and a height value, and CGRect has an origin field, a CGPoint, and a size field, a CGSize. Together the origin and size fields define the position and size of a rectangle.

The CGGeometry Reference also defines other types, such as CGFloat and CGVector. CGFloat is nothing more than a typedef for float or double, depending on the architecture the application runs on, 32-bit or 64-bit.

2. Frames and Bounds

The first thing I want to clarify is the difference between a view's frame and its bounds, because this is something that trips up a lot of beginning iOS developers. The difference isn't difficult though.

On iOS and OS X, an application has multiple coordinate systems. On iOS, for example, the application's window is positioned in the screen's coordinate system and every subview of the window is positioned in the window's coordinate system. In other words, the subviews of a view are always positioned in the view's coordinate system.

Frame

As the documentation clarifies, the frame of a view is a structure, a CGRect, that defines the size of the view and its position in the view's superview, the superview's coordinate system. Take a look at the following diagram for clarification.

Bounds

The bounds property of a view defines the size of the view and its position in the view's own coordinate system. This means that in most cases the origin of the bounds of a view are set to {0,0} as shown in the following diagram. The view's bounds is important for drawing the view.

When the frame property of a view is modified, the view's center and/or bounds are also modified.

3. CGGeometry Reference

Convenient Getters

As I mentioned earlier, the CGGeometry Reference is a collection of structures, constants, and functions that make it easier to work with coordinates and rectangles. You may have run into code snippets similar to this:

CGPoint point = CGPointMake(self.view.frame.origin.x + self.view.frame.size.width, self.view.frame.origin.y + self.view.frame.size.height);

Not only is this snippet hard to read, it's also quite verbose. We can rewrite this code snippet using two convenient functions defined in the CGGeometry Reference.

CGRect frame = self.view.frame;
CGPoint point = CGPointMake(CGRectGetMaxX(frame), CGRectGetMaxY(frame));

To simplify the above code snippet, we store the view's frame in a variable named frame and use CGRectGetMaxX and CGRectGetMaxY. The names of the functions are self-explanatory.

The CGGeometry Reference defines functions to return the smallest and largest values for the x- and y-coordinates of a rectangle as well as the x- and y-coordinates that lie at the rectangle's center. Two other convenient getter functions are CGRectGetWidth and CGRectGetHeight.

Creating Structures

When it comes to creating CGPoint, CGSize, and CGRect structures, most of us use CGPointMake and its cousins. These functions are also defined in the CGGeometry Reference. Even though their implementation is surprisingly easy, they are incredibly useful and make you write less code. For example, this is how CGRectMake is actually implemented:

CGRectMake(CGFloat x, CGFloat y, CGFloat width, CGFloat height)
{
  CGRect rect;
  rect.origin.x = x; rect.origin.y = y;
  rect.size.width = width; rect.size.height = height;
  return rect;
}

Modifying Rectangles

The functions we've covered so far are pretty well known among iOS developers and help us write less code that is more readable. However, the CGGeometry Reference also defines a number of other functions that are less known. For example, the CGGeometry Reference defines half a dozen functions for modifying CGRect structures. Let's take a look at a few of these functions.

CGRectUnion

CGRectUnion accepts two CGRect structures and returns the smallest possible rectangle that contains both rectangles. This may sound trivial and I'm sure you can easily accomplish the same task with a few lines of code, but CGGeometry is all about providing you with a few dozen functions that make your code cleaner and more readable.

If you add the following code snippet to a view controller's viewDidLoad method, you should get the following result in the iOS Simulator. The gray rectangle is the result of using CGRectUnion.

// CGRectUnion
CGRect frame1 = CGRectMake(80.0, 100.0, 150.0, 240.0);
CGRect frame2 = CGRectMake(140.0, 240.0, 120.0, 120.0);
CGRect frame3 = CGRectUnion(frame1, frame2);

UIView *view1 = [[UIView alloc] initWithFrame:frame1];
[view1 setBackgroundColor:[UIColor redColor]];

UIView *view2 = [[UIView alloc] initWithFrame:frame2];
[view2 setBackgroundColor:[UIColor orangeColor]];

UIView *view3 = [[UIView alloc] initWithFrame:frame3];
[view3 setBackgroundColor:[UIColor grayColor]];

[self.view addSubview:view3];
[self.view addSubview:view2];
[self.view addSubview:view1];

CGRectDivide

Another useful function is CGRectDivide, which lets you divide a given rectangle into two rectangles. Take a look at the following code snippet and screenshot to see how it's used.

// CGRectDivide
CGRect frame = CGRectMake(10.0, 50.0, 300.0, 300.0);
CGRect part1;
CGRect part2;
CGRectDivide(frame, &part1, &part2, 100.0, CGRectMaxYEdge);

UIView *view1 = [[UIView alloc] initWithFrame:frame];
[view1 setBackgroundColor:[UIColor grayColor]];

UIView *view2 = [[UIView alloc] initWithFrame:part1];
[view2 setBackgroundColor:[UIColor orangeColor]];

UIView *view3 = [[UIView alloc] initWithFrame:part2];
[view3 setBackgroundColor:[UIColor redColor]];

[self.view addSubview:view1];
[self.view addSubview:view2];
[self.view addSubview:view3];

If you were to calculate the red and orange rectangle without using CGRectDivide, you'd end up with a few dozen lines of code. Give it a try if you don't believe me.

Comparison and Containment

Comparing geometric structure and checking for membership is very easy with the following six functions:

  • CGPointEqualToPoint
  • CGSizeEqualToSize
  • CGRectEqualToRect
  • CGRectIntersectsRect
  • CGRectContainsPoint
  • CGRectContainsRect

The CGGeometry Reference has a few other gems, such as CGPointCreateDictionaryRepresentation for converting a CGPoint structure to a CFDictionaryRef, and CGRectIsEmpty to check if a rectangle's width and height are equal to 0.0. Read the documentation of the CGGeometry Reference to find out more.

4. Bonus: Logging

Logging structures to Xcode's console is cumbersome without a few helper functions. Luckily, the UIKit framework defines a handful of functions that make this very easy to do. I use them all the time. Take a look at the following code snippet to see how they work. It's no rocket science.

CGPoint point = CGPointMake(10.0, 25.0);
CGSize size = CGSizeMake(103.0, 223.0);
CGRect frame = CGRectMake(point.x, point.y, size.width, size.height);
NSLog(@"\n%@\n%@\n%@", NSStringFromCGPoint(point), NSStringFromCGSize(size), NSStringFromCGRect(frame));

There are also convenience functions for logging affine transforms (NSStringFromCGAffineTransform), edge insets structs (NSStringFromUIEdgeInsets), and offset structs (NSStringFromUIOffset).

Conclusion

The iOS SDK contains a lot of gems many developers don't know about. I hope I've convinced you of the usefulness of the CGGeometry Reference. Once you start using its collection of functions, you'll start wondering how you used to manage without it.

Advertisement