Custom Drawing in drawRect:

So far, you have subclassed UIView, created instances of the subclass, inserted them into the view hierarchy, and specified their frames and backgroundColors. In this section, you will write the custom drawing code for BNRHypnosisView in its drawRect: method.

The drawRect: method is the rendering step where a view draws itself onto its layer. UIView subclasses override drawRect: to perform custom drawing. For example, the drawRect: method of UIButton draws light-blue text centered in a rectangle.

The first thing that you typically do when overriding drawRect: is get the bounds rectangle of the view. The bounds property, inherited from UIView, is the rectangle that defines the area where the view will draw itself.

Each view has a coordinate system that it uses when drawing itself. The bounds is a view’s rectangle in its own coordinate system. The frame is the same rectangle in its superview’s coordinate system.

You might be wondering, Why do we need another rectangle when we already have frame?

The frame and bounds rectangles have distinct purposes. A view’s frame rectangle is used during compositing to lay out the view’s layer relative to the rest of the view hierarchy. The bounds rectangle is used during the rendering step to lay out detailed drawing within the boundaries of the view’s layer. (Figure 4.14).

Figure 4.14  bounds vs. frame

bounds vs. frame

You can use the bounds property of the window to define the frame for a full-screen instance of BNRHypnosisView.

In BNRAppDelegate.m, update firstView’s frame to match the bounds of the window.

-​ ​(​B​O​O​L​)​a​p​p​l​i​c​a​t​i​o​n​:​(​U​I​A​p​p​l​i​c​a​t​i​o​n​ ​*​)​a​p​p​l​i​c​a​t​i​o​n​
 ​ ​ ​ ​d​i​d​F​i​n​i​s​h​L​a​u​n​c​h​i​n​g​W​i​t​h​O​p​t​i​o​n​s​:​(​N​S​D​i​c​t​i​o​n​a​r​y​ ​*​)​l​a​u​n​c​h​O​p​t​i​o​n​s​
{​
 ​ ​ ​ ​s​e​l​f​.​w​i​n​d​o​w​ ​=​ ​[​[​U​I​W​i​n​d​o​w​ ​a​l​l​o​c​]​ ​i​n​i​t​W​i​t​h​F​r​a​m​e​:​[​[​U​I​S​c​r​e​e​n​ ​m​a​i​n​S​c​r​e​e​n​]​ ​b​o​u​n​d​s​]​]​;​
 ​ ​ ​ ​/​/​ ​O​v​e​r​r​i​d​e​ ​p​o​i​n​t​ ​f​o​r​ ​c​u​s​t​o​m​i​z​a​t​i​o​n​ ​a​f​t​e​r​ ​a​p​p​l​i​c​a​t​i​o​n​ ​l​a​u​n​c​h​
 ​ ​ ​ ​C​G​R​e​c​t​ ​f​i​r​s​t​F​r​a​m​e​ ​=​ ​C​G​R​e​c​t​M​a​k​e​(​1​6​0​,​ ​2​4​0​,​ ​1​0​0​,​ ​1​5​0​)​;​
 ​ ​ ​ ​C​G​R​e​c​t​ ​f​i​r​s​t​F​r​a​m​e​ ​=​ ​s​e​l​f​.​w​i​n​d​o​w​.​b​o​u​n​d​s​;​

 ​ ​ ​ ​B​N​R​H​y​p​n​o​s​i​s​V​i​e​w​ ​*​f​i​r​s​t​V​i​e​w​ ​=​ ​[​[​B​N​R​H​y​p​n​o​s​i​s​V​i​e​w​ ​a​l​l​o​c​]​ ​i​n​i​t​W​i​t​h​F​r​a​m​e​:​f​i​r​s​t​F​r​a​m​e​]​;​
 ​ ​ ​ ​[​s​e​l​f​.​w​i​n​d​o​w​ ​a​d​d​S​u​b​v​i​e​w​:​f​i​r​s​t​V​i​e​w​]​;​

 ​ ​ ​ ​s​e​l​f​.​w​i​n​d​o​w​.​b​a​c​k​g​r​o​u​n​d​C​o​l​o​r​ ​=​ ​[​U​I​C​o​l​o​r​ ​w​h​i​t​e​C​o​l​o​r​]​;​
 ​ ​ ​ ​[​s​e​l​f​.​w​i​n​d​o​w​ ​m​a​k​e​K​e​y​A​n​d​V​i​s​i​b​l​e​]​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​Y​E​S​;​
}​

Build and run the application, and you will be greeted with a full-sized view with a red background.

Drawing a single circle

You are going to ease into the drawing code by drawing a single circle – the largest that will fit within the bounds of the view.

In BNRHypnosisView.m, add code to drawRect: that finds the center point of bounds.

-​ ​(​v​o​i​d​)​d​r​a​w​R​e​c​t​:​(​C​G​R​e​c​t​)​r​e​c​t​
{​
 ​ ​ ​ ​C​G​R​e​c​t​ ​b​o​u​n​d​s​ ​=​ ​s​e​l​f​.​b​o​u​n​d​s​;​

 ​ ​ ​ ​/​/​ ​F​i​g​u​r​e​ ​o​u​t​ ​t​h​e​ ​c​e​n​t​e​r​ ​o​f​ ​t​h​e​ ​b​o​u​n​d​s​ ​r​e​c​t​a​n​g​l​e​
 ​ ​ ​ ​C​G​P​o​i​n​t​ ​c​e​n​t​e​r​;​
 ​ ​ ​ ​c​e​n​t​e​r​.​x​ ​=​ ​b​o​u​n​d​s​.​o​r​i​g​i​n​.​x​ ​+​ ​b​o​u​n​d​s​.​s​i​z​e​.​w​i​d​t​h​ ​/​ ​2​.​0​;​
 ​ ​ ​ ​c​e​n​t​e​r​.​y​ ​=​ ​b​o​u​n​d​s​.​o​r​i​g​i​n​.​y​ ​+​ ​b​o​u​n​d​s​.​s​i​z​e​.​h​e​i​g​h​t​ ​/​ ​2​.​0​;​
}​

Next, set the radius for your circle to be half of the smaller of the view’s dimensions. (Determining the smaller dimension will draw the right circle in portrait and landscape orientations.)

-​ ​(​v​o​i​d​)​d​r​a​w​R​e​c​t​:​(​C​G​R​e​c​t​)​r​e​c​t​
{​
 ​ ​ ​ ​C​G​R​e​c​t​ ​b​o​u​n​d​s​ ​=​ ​s​e​l​f​.​b​o​u​n​d​s​;​

 ​ ​ ​ ​/​/​ ​F​i​g​u​r​e​ ​o​u​t​ ​t​h​e​ ​c​e​n​t​e​r​ ​o​f​ ​t​h​e​ ​b​o​u​n​d​s​ ​r​e​c​t​a​n​g​l​e​
 ​ ​ ​ ​C​G​P​o​i​n​t​ ​c​e​n​t​e​r​;​
 ​ ​ ​ ​c​e​n​t​e​r​.​x​ ​=​ ​b​o​u​n​d​s​.​o​r​i​g​i​n​.​x​ ​+​ ​b​o​u​n​d​s​.​s​i​z​e​.​w​i​d​t​h​ ​/​ ​2​.​0​;​
 ​ ​ ​ ​c​e​n​t​e​r​.​y​ ​=​ ​b​o​u​n​d​s​.​o​r​i​g​i​n​.​y​ ​+​ ​b​o​u​n​d​s​.​s​i​z​e​.​h​e​i​g​h​t​ ​/​ ​2​.​0​;​

 ​ ​ ​ ​/​/​ ​T​h​e​ ​c​i​r​c​l​e​ ​w​i​l​l​ ​b​e​ ​t​h​e​ ​l​a​r​g​e​s​t​ ​t​h​a​t​ ​w​i​l​l​ ​f​i​t​ ​i​n​ ​t​h​e​ ​v​i​e​w​
 ​ ​ ​ ​f​l​o​a​t​ ​r​a​d​i​u​s​ ​=​ ​(​M​I​N​(​b​o​u​n​d​s​.​s​i​z​e​.​w​i​d​t​h​,​ ​b​o​u​n​d​s​.​s​i​z​e​.​h​e​i​g​h​t​)​ ​/​ ​2​.​0​)​;​

}​

UIBezierPath

The next step is to draw the circle using the UIBezierPath class. Instances of this class define and draw lines and curves that you can use to make shapes, like circles.

First, create an instance of UIBezierPath.

-​ ​(​v​o​i​d​)​d​r​a​w​R​e​c​t​:​(​C​G​R​e​c​t​)​r​e​c​t​
{​
 ​ ​ ​ ​.​.​.​

 ​ ​ ​ ​/​/​ ​T​h​e​ ​c​i​r​c​l​e​ ​w​i​l​l​ ​b​e​ ​t​h​e​ ​l​a​r​g​e​s​t​ ​t​h​a​t​ ​w​i​l​l​ ​f​i​t​ ​i​n​ ​t​h​e​ ​v​i​e​w​
 ​ ​ ​ ​f​l​o​a​t​ ​r​a​d​i​u​s​ ​=​ ​(​M​I​N​(​b​o​u​n​d​s​.​s​i​z​e​.​w​i​d​t​h​,​ ​b​o​u​n​d​s​.​s​i​z​e​.​h​e​i​g​h​t​)​ ​/​ ​2​.​0​)​;​
 ​ ​ ​ ​
 ​ ​ ​ ​U​I​B​e​z​i​e​r​P​a​t​h​ ​*​p​a​t​h​ ​=​ ​[​[​U​I​B​e​z​i​e​r​P​a​t​h​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
 ​ ​ ​ ​
}​

The next step is defining the path that the UIBezierPath object should follow. How do you define a circle-shaped path? The best place to find an answer to this question is the UIBezierPath class reference in Apple’s developer documentation.

Using the developer documentation

From Xcode’s menu, select HelpDocumentation and API Reference. You can also use the keyboard shortcut Option-Command-? (be sure to hold down the Shift key, too, to get the ‘?’).

(When you access the documentation, Xcode may try to go get the latest for you from Apple. You may be asked for your Apple ID and password.)

When the documentation browser opens, search for UIBezierPath. You will be offered several results. Find and select UIBezierPath Class Reference.

Figure 4.15  Documentation results

Documentation results

This page opens to an overview of the class, which is interesting, but let’s stay focused on your circle-shaped path question. The lefthand side of the reference is the table of contents. (If you do not see a table of contents, select the Documentation results icon at the top left of the browser.)

In the table of contents, find the Tasks section. This is a good place to begin the hunt for a method that does something specific. The first task is Creating a UIBezierPath Object. You have already done that, so take a look at the second task: Constructing a Path. Select this task, and you will see a list of relevant UIBezierPath methods.

Figure 4.16  Methods for constructing a path

Methods for constructing a path

A likely candidate for a circular path is addArcWithCenter:radius:startAngle:endAngle:clockwise:. Click this method to see more details about its parameters. You have already computed the center and the radius. The start and end angle values are in radians. To draw a circle, you will use 0 for the start angle and M_PI * 2 for the end angle. (If your trigonometry is rusty, you can take our word on this or click the Figure 1 link within the Discussion of this method’s documentation to see a diagram of the unit circle.) Finally, because you are drawing a complete circle, the clockwise parameter will not matter. It is a required parameter, however, so you will need to give it a value.

In BNRHypnosisView.m, send a message to the UIBezierPath that defines its path.

-​ ​(​v​o​i​d​)​d​r​a​w​R​e​c​t​:​(​C​G​R​e​c​t​)​r​e​c​t​
{​
 ​ ​ ​ ​C​G​R​e​c​t​ ​b​o​u​n​d​s​ ​=​ ​s​e​l​f​.​b​o​u​n​d​s​;​

 ​ ​ ​ ​/​/​ ​F​i​g​u​r​e​ ​o​u​t​ ​t​h​e​ ​c​e​n​t​e​r​ ​o​f​ ​t​h​e​ ​b​o​u​n​d​s​ ​r​e​c​t​a​n​g​l​e​
 ​ ​ ​ ​C​G​P​o​i​n​t​ ​c​e​n​t​e​r​;​
 ​ ​ ​ ​c​e​n​t​e​r​.​x​ ​=​ ​b​o​u​n​d​s​.​o​r​i​g​i​n​.​x​ ​+​ ​b​o​u​n​d​s​.​s​i​z​e​.​w​i​d​t​h​ ​/​ ​2​.​0​;​
 ​ ​ ​ ​c​e​n​t​e​r​.​y​ ​=​ ​b​o​u​n​d​s​.​o​r​i​g​i​n​.​y​ ​+​ ​b​o​u​n​d​s​.​s​i​z​e​.​h​e​i​g​h​t​ ​/​ ​2​.​0​;​

 ​ ​ ​ ​/​/​ ​T​h​e​ ​c​i​r​c​l​e​ ​w​i​l​l​ ​b​e​ ​t​h​e​ ​l​a​r​g​e​s​t​ ​t​h​a​t​ ​w​i​l​l​ ​f​i​t​ ​i​n​ ​t​h​e​ ​v​i​e​w​
 ​ ​ ​ ​f​l​o​a​t​ ​r​a​d​i​u​s​ ​=​ ​(​M​I​N​(​b​o​u​n​d​s​.​s​i​z​e​.​w​i​d​t​h​,​ ​b​o​u​n​d​s​.​s​i​z​e​.​h​e​i​g​h​t​)​ ​/​ ​2​.​0​)​;​

 ​ ​ ​ ​U​I​B​e​z​i​e​r​P​a​t​h​ ​*​p​a​t​h​ ​=​ ​[​[​U​I​B​e​z​i​e​r​P​a​t​h​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
 ​ ​ ​ ​
 ​ ​ ​ ​/​/​ ​A​d​d​ ​a​n​ ​a​r​c​ ​t​o​ ​t​h​e​ ​p​a​t​h​ ​a​t​ ​c​e​n​t​e​r​,​ ​w​i​t​h​ ​r​a​d​i​u​s​ ​o​f​ ​r​a​d​i​u​s​,​
 ​ ​ ​ ​/​/​ ​f​r​o​m​ ​0​ ​t​o​ ​2​*​P​I​ ​r​a​d​i​a​n​s​ ​(​a​ ​c​i​r​c​l​e​)​
 ​ ​ ​ ​[​p​a​t​h​ ​a​d​d​A​r​c​W​i​t​h​C​e​n​t​e​r​:​c​e​n​t​e​r​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​a​d​i​u​s​:​r​a​d​i​u​s​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​t​a​r​t​A​n​g​l​e​:​0​.​0​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​n​d​A​n​g​l​e​:​M​_​P​I​ ​*​ ​2​.​0​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​l​o​c​k​w​i​s​e​:​Y​E​S​]​;​
}​

You have defined a path, but you have not drawn anything yet. Back in the UIBezier class reference, find and select the Drawing Paths task. From these methods, the best choice is stroke. (The other methods either fill in the entire shape or require a CGBlendMode that you do not need.)

In BNRHypnosisView.m, send a message to the UIBezierPath that tells it to draw.

-​ ​(​v​o​i​d​)​d​r​a​w​R​e​c​t​:​(​C​G​R​e​c​t​)​r​e​c​t​
{​
 ​ ​ ​ ​.​.​.​

 ​ ​ ​ ​U​I​B​e​z​i​e​r​P​a​t​h​ ​*​p​a​t​h​ ​=​ ​[​[​U​I​B​e​z​i​e​r​P​a​t​h​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​/​/​ ​A​d​d​ ​a​n​ ​a​r​c​ ​t​o​ ​t​h​e​ ​p​a​t​h​ ​a​t​ ​c​e​n​t​e​r​,​ ​w​i​t​h​ ​r​a​d​i​u​s​ ​o​f​ ​r​a​d​i​u​s​,​
 ​ ​ ​ ​/​/​ ​f​r​o​m​ ​0​ ​t​o​ ​2​*​P​I​ ​r​a​d​i​a​n​s​ ​(​a​ ​c​i​r​c​l​e​)​
 ​ ​ ​ ​[​p​a​t​h​ ​a​d​d​A​r​c​W​i​t​h​C​e​n​t​e​r​:​c​e​n​t​e​r​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​a​d​i​u​s​:​r​a​d​i​u​s​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​t​a​r​t​A​n​g​l​e​:​0​.​0​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​n​d​A​n​g​l​e​:​M​_​P​I​ ​*​ ​2​.​0​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​l​o​c​k​w​i​s​e​:​Y​E​S​]​;​
 ​ ​ ​ ​ ​
 ​ ​ ​ ​/​/​ ​D​r​a​w​ ​t​h​e​ ​l​i​n​e​!​
 ​ ​ ​ ​[​p​a​t​h​ ​s​t​r​o​k​e​]​;​
 ​ ​ ​ ​
}​

Build and run the application, and you will see a thin, black outline of a circle that is as wide as the screen (or as tall if you are in landscape orientation).

Figure 4.17  BNRHypnosisView with a single circle

BNRHypnosisView with a single circle

Based on the original plan for Hypnosister, the line describing your circle is not yet right. It should be wider and light gray.

To see how to fix these issues, return to the UIBezierPath reference. In the table of contents, find the Properties section. One of these properties should stand out as useful in this case – lineWidth. Select this property. You will see that lineWidth is of type CGFloat and that its default is 1.0.

In BNRHypnosisView.m, make the width of the line 10 points.

-​ ​(​v​o​i​d​)​d​r​a​w​R​e​c​t​:​(​C​G​R​e​c​t​)​r​e​c​t​
{​
 ​ ​ ​ ​.​.​.​

 ​ ​ ​ ​/​/​ ​A​d​d​ ​a​n​ ​a​r​c​ ​t​o​ ​t​h​e​ ​p​a​t​h​ ​a​t​ ​c​e​n​t​e​r​,​ ​w​i​t​h​ ​r​a​d​i​u​s​ ​o​f​ ​r​a​d​i​u​s​,​
 ​ ​ ​ ​/​/​ ​f​r​o​m​ ​0​ ​t​o​ ​2​*​P​I​ ​r​a​d​i​a​n​s​ ​(​a​ ​c​i​r​c​l​e​)​
 ​ ​ ​ ​[​p​a​t​h​ ​a​d​d​A​r​c​W​i​t​h​C​e​n​t​e​r​:​c​e​n​t​e​r​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​a​d​i​u​s​:​r​a​d​i​u​s​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​t​a​r​t​A​n​g​l​e​:​0​.​0​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​n​d​A​n​g​l​e​:​M​_​P​I​ ​*​ ​2​.​0​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​l​o​c​k​w​i​s​e​:​Y​E​S​]​;​

 ​ ​ ​ ​/​/​ ​C​o​n​f​i​g​u​r​e​ ​l​i​n​e​ ​w​i​d​t​h​ ​t​o​ ​1​0​ ​p​o​i​n​t​s​
 ​ ​ ​ ​p​a​t​h​.​l​i​n​e​W​i​d​t​h​ ​=​ ​1​0​;​

 ​ ​ ​ ​/​/​ ​D​r​a​w​ ​t​h​e​ ​l​i​n​e​!​
 ​ ​ ​ ​[​p​a​t​h​ ​s​t​r​o​k​e​]​;​
}​

Build and run the application to confirm that the line is now wider.

There is no property in UIBezierPath that deals with the color of the line. But there is a clue in the class overview. Use the table of contents to return to the Overview. In the fifth paragraph (as of this writing), there is a parenthetical aside that reads, You set the stroke and fill color using the UIColor class.

The UIColor class is linked, so you can click it to be taken directly to the UIColor class reference. In UIColor’s Tasks section, select Drawing Operations and browse through the associated methods. For your purposes, you could use either set or setStroke. You will use setStroke to make your code more obvious to others.

The setStroke method is an instance method, so you need an instance of UIColor to send it to. Recall that UIColor has convenience methods that return common colors. You can see these methods listed under the Class Methods section of the UIColor reference, including one named lightGrayColor.

Now you have the information you need. In BNRHypnosisView.m, add code to create a light gray UIColor instance and send it the setStroke message so that when the path is drawn, it will be drawn in light gray.

-​ ​(​v​o​i​d​)​d​r​a​w​R​e​c​t​:​(​C​G​R​e​c​t​)​r​e​c​t​
{​
 ​ ​ ​ ​.​.​.​

 ​ ​ ​ ​/​/​ ​C​o​n​f​i​g​u​r​e​ ​l​i​n​e​ ​w​i​d​t​h​ ​t​o​ ​1​0​ ​p​o​i​n​t​s​
 ​ ​ ​ ​p​a​t​h​.​l​i​n​e​W​i​d​t​h​ ​=​ ​1​0​;​

 ​ ​ ​ ​/​/​ ​C​o​n​f​i​g​u​r​e​ ​t​h​e​ ​d​r​a​w​i​n​g​ ​c​o​l​o​r​ ​t​o​ ​l​i​g​h​t​ ​g​r​a​y​
 ​ ​ ​ ​[​[​U​I​C​o​l​o​r​ ​l​i​g​h​t​G​r​a​y​C​o​l​o​r​]​ ​s​e​t​S​t​r​o​k​e​]​;​

 ​ ​ ​ ​/​/​ ​D​r​a​w​ ​t​h​e​ ​l​i​n​e​!​
 ​ ​ ​ ​[​p​a​t​h​ ​s​t​r​o​k​e​]​;​
}​

Build and run the application, and you will see a wider, light gray outline of a circle.

By now, you will have noticed that a view’s backgroundColor is drawn regardless of what drawRect: does. Often, you will set the backgroundColor of a custom view to be transparent, or clear-colored, so that only the results of drawRect: show.

In BNRAppDelegate.m, remove the code that sets the background color of the view.

B​N​R​H​y​p​n​o​s​i​s​V​i​e​w​ ​*​f​i​r​s​t​V​i​e​w​ ​=​ ​[​[​B​N​R​H​y​p​n​o​s​i​s​V​i​e​w​ ​a​l​l​o​c​]​ ​i​n​i​t​W​i​t​h​F​r​a​m​e​:​f​i​r​s​t​F​r​a​m​e​]​;​
f​i​r​s​t​V​i​e​w​.​b​a​c​k​g​r​o​u​n​d​C​o​l​o​r​ ​=​ ​[​U​I​C​o​l​o​r​ ​r​e​d​C​o​l​o​r​]​;​

[​s​e​l​f​.​w​i​n​d​o​w​ ​a​d​d​S​u​b​v​i​e​w​:​v​i​e​w​]​;​

Then, in BNRHypnosisView.m, add code to initWithFrame: to set the background color of every BNRHypnosisView to clear.

-​ ​(​i​n​s​t​a​n​c​e​t​y​p​e​)​i​n​i​t​W​i​t​h​F​r​a​m​e​:​(​C​G​R​e​c​t​)​f​r​a​m​e​
{​
 ​ ​ ​ ​s​e​l​f​ ​=​ ​[​s​u​p​e​r​ ​i​n​i​t​W​i​t​h​F​r​a​m​e​:​f​r​a​m​e​]​;​
 ​ ​ ​ ​i​f​ ​(​s​e​l​f​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​A​l​l​ ​B​N​R​H​y​p​n​o​s​i​s​V​i​e​w​s​ ​s​t​a​r​t​ ​w​i​t​h​ ​a​ ​c​l​e​a​r​ ​b​a​c​k​g​r​o​u​n​d​ ​c​o​l​o​r​
 ​ ​ ​ ​ ​ ​ ​ ​s​e​l​f​.​b​a​c​k​g​r​o​u​n​d​C​o​l​o​r​ ​=​ ​[​U​I​C​o​l​o​r​ ​c​l​e​a​r​C​o​l​o​r​]​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​s​e​l​f​;​
}​

Build and run the application. Figure 4.18 shows the clear background and the resulting circle.

Figure 4.18  BNRHypnosisView with clear background

BNRHypnosisView with clear background

Drawing concentric circles

There are two approaches you can take to draw multiple concentric circles inside the BNRHypnosisView. You can create multiple instances of UIBezierPath, each one representing one circle. Or you can add multiple circles to the single instance of UIBezierPath, and each circle will be a sub-path. It is slightly more efficient to use one instance, so you are going to do that.

To fill the screen with concentric circles, you need to determine the radius of the outermost circle. You will start drawing a circle with this radius and then draw circles with a decreasing radius for as long as the radius remains positive.

For the maximum radius, you are going to use half of the hypotenuse of the entire view. This means that the outermost circle will nearly circumscribe the view, and you will only see bits of light gray in the corners.

In BNRHypnosisView.m, replace the code that draws one circle with code that draws concentric circles.

-​ ​(​v​o​i​d​)​d​r​a​w​R​e​c​t​:​(​C​G​R​e​c​t​)​r​e​c​t​
{​
 ​ ​ ​ ​C​G​R​e​c​t​ ​b​o​u​n​d​s​ ​=​ ​s​e​l​f​.​b​o​u​n​d​s​;​

 ​ ​ ​ ​/​/​ ​F​i​g​u​r​e​ ​o​u​t​ ​t​h​e​ ​c​e​n​t​e​r​ ​o​f​ ​t​h​e​ ​b​o​u​n​d​s​ ​r​e​c​t​a​n​g​l​e​
 ​ ​ ​ ​C​G​P​o​i​n​t​ ​c​e​n​t​e​r​;​
 ​ ​ ​ ​c​e​n​t​e​r​.​x​ ​=​ ​b​o​u​n​d​s​.​o​r​i​g​i​n​.​x​ ​+​ ​b​o​u​n​d​s​.​s​i​z​e​.​w​i​d​t​h​ ​/​ ​2​.​0​;​
 ​ ​ ​ ​c​e​n​t​e​r​.​y​ ​=​ ​b​o​u​n​d​s​.​o​r​i​g​i​n​.​y​ ​+​ ​b​o​u​n​d​s​.​s​i​z​e​.​h​e​i​g​h​t​ ​/​ ​2​.​0​;​

 ​ ​ ​ ​/​/​ ​T​h​e​ ​c​i​r​c​l​e​ ​w​i​l​l​ ​b​e​ ​t​h​e​ ​l​a​r​g​e​s​t​ ​t​h​a​t​ ​w​i​l​l​ ​f​i​t​ ​i​n​ ​t​h​e​ ​v​i​e​w​
 ​ ​ ​ ​f​l​o​a​t​ ​r​a​d​i​u​s​ ​=​ ​(​M​I​N​(​b​o​u​n​d​s​.​s​i​z​e​.​w​i​d​t​h​,​ ​b​o​u​n​d​s​.​s​i​z​e​.​h​e​i​g​h​t​)​ ​/​ ​2​.​0​)​;​

 ​ ​ ​ ​/​/​ ​T​h​e​ ​l​a​r​g​e​s​t​ ​c​i​r​c​l​e​ ​w​i​l​l​ ​c​i​r​c​u​m​s​c​r​i​b​e​ ​t​h​e​ ​v​i​e​w​
 ​ ​ ​ ​f​l​o​a​t​ ​m​a​x​R​a​d​i​u​s​ ​=​ ​h​y​p​o​t​(​b​o​u​n​d​s​.​s​i​z​e​.​w​i​d​t​h​,​ ​b​o​u​n​d​s​.​s​i​z​e​.​h​e​i​g​h​t​)​ ​/​ ​2​.​0​;​

 ​ ​ ​ ​U​I​B​e​z​i​e​r​P​a​t​h​ ​*​p​a​t​h​ ​=​ ​[​[​U​I​B​e​z​i​e​r​P​a​t​h​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​/​/​ ​A​d​d​ ​a​n​ ​a​r​c​ ​t​o​ ​t​h​e​ ​p​a​t​h​ ​a​t​ ​c​e​n​t​e​r​,​ ​w​i​t​h​ ​r​a​d​i​u​s​ ​o​f​ ​r​a​d​i​u​s​,​
 ​ ​ ​ ​/​/​ ​f​r​o​m​ ​0​ ​t​o​ ​2​*​P​I​ ​r​a​d​i​a​n​s​ ​(​a​ ​c​i​r​c​l​e​)​
 ​ ​ ​ ​[​p​a​t​h​ ​a​d​d​A​r​c​W​i​t​h​C​e​n​t​e​r​:​c​e​n​t​e​r​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​a​d​i​u​s​:​r​a​d​i​u​s​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​t​a​r​t​A​n​g​l​e​:​0​.​0​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​n​d​A​n​g​l​e​:​M​_​P​I​ ​*​ ​2​.​0​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​l​o​c​k​w​i​s​e​:​Y​E​S​]​;​

 ​ ​ ​ ​f​o​r​ ​(​f​l​o​a​t​ ​c​u​r​r​e​n​t​R​a​d​i​u​s​ ​=​ ​m​a​x​R​a​d​i​u​s​;​ ​c​u​r​r​e​n​t​R​a​d​i​u​s​ ​>​ ​0​;​ ​c​u​r​r​e​n​t​R​a​d​i​u​s​ ​-​=​ ​2​0​)​ ​{​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​p​a​t​h​ ​a​d​d​A​r​c​W​i​t​h​C​e​n​t​e​r​:​c​e​n​t​e​r​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​a​d​i​u​s​:​c​u​r​r​e​n​t​R​a​d​i​u​s​ ​/​/​ ​N​o​t​e​ ​t​h​i​s​ ​i​s​ ​c​u​r​r​e​n​t​R​a​d​i​u​s​!​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​t​a​r​t​A​n​g​l​e​:​0​.​0​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​n​d​A​n​g​l​e​:​M​_​P​I​ ​*​ ​2​.​0​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​l​o​c​k​w​i​s​e​:​Y​E​S​]​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​C​o​n​f​i​g​u​r​e​ ​l​i​n​e​ ​w​i​d​t​h​ ​t​o​ ​1​0​ ​p​o​i​n​t​s​
 ​ ​ ​ ​p​a​t​h​.​l​i​n​e​W​i​d​t​h​ ​=​ ​1​0​.​0​;​

 ​ ​ ​ ​/​/​ ​D​r​a​w​ ​t​h​e​ ​l​i​n​e​!​
 ​ ​ ​ ​[​p​a​t​h​ ​s​t​r​o​k​e​]​;​
}​

Build and run the application. It is not quite what you were expecting; it looks more like crop circles than concentric circles (Figure 4.19).

Figure 4.19  BNRHypnosisView drawing crop circles

BNRHypnosisView drawing crop circles

The problem is that your single UIBezierPath object is connecting the sub-paths (the individual circles) to form the complete path. Think of a UIBezierPath object as a pencil on a piece of paper – when you go to draw another circle, the pencil stays on the piece of paper. You need to lift the pencil off the piece of paper before drawing a new circle.

In the for loop in BNRHypnosisView’s drawRect:, pick up the pencil and move it to the correct spot before drawing each circle.

-​ ​(​v​o​i​d​)​d​r​a​w​R​e​c​t​:​(​C​G​R​e​c​t​)​r​e​c​t​
{​
 ​ ​ ​ ​C​G​R​e​c​t​ ​b​o​u​n​d​s​ ​=​ ​s​e​l​f​.​b​o​u​n​d​s​;​

 ​ ​ ​ ​/​/​ ​F​i​g​u​r​e​ ​o​u​t​ ​t​h​e​ ​c​e​n​t​e​r​ ​o​f​ ​t​h​e​ ​b​o​u​n​d​s​ ​r​e​c​t​a​n​g​l​e​
 ​ ​ ​ ​C​G​P​o​i​n​t​ ​c​e​n​t​e​r​;​
 ​ ​ ​ ​c​e​n​t​e​r​.​x​ ​=​ ​b​o​u​n​d​s​.​o​r​i​g​i​n​.​x​ ​+​ ​b​o​u​n​d​s​.​s​i​z​e​.​w​i​d​t​h​ ​/​ ​2​.​0​;​
 ​ ​ ​ ​c​e​n​t​e​r​.​y​ ​=​ ​b​o​u​n​d​s​.​o​r​i​g​i​n​.​y​ ​+​ ​b​o​u​n​d​s​.​s​i​z​e​.​h​e​i​g​h​t​ ​/​ ​2​.​0​;​

 ​ ​ ​ ​/​/​ ​T​h​e​ ​l​a​r​g​e​s​t​ ​c​i​r​c​l​e​ ​w​i​l​l​ ​c​i​r​c​u​m​s​c​r​i​b​e​ ​t​h​e​ ​v​i​e​w​
 ​ ​ ​ ​f​l​o​a​t​ ​m​a​x​R​a​d​i​u​s​ ​=​ ​h​y​p​o​t​(​b​o​u​n​d​s​.​s​i​z​e​.​w​i​d​t​h​,​ ​b​o​u​n​d​s​.​s​i​z​e​.​h​e​i​g​h​t​)​ ​/​ ​2​.​0​;​

 ​ ​ ​ ​U​I​B​e​z​i​e​r​P​a​t​h​ ​*​p​a​t​h​ ​=​ ​[​[​U​I​B​e​z​i​e​r​P​a​t​h​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​f​o​r​ ​(​f​l​o​a​t​ ​c​u​r​r​e​n​t​R​a​d​i​u​s​ ​=​ ​m​a​x​R​a​d​i​u​s​;​ ​c​u​r​r​e​n​t​R​a​d​i​u​s​ ​>​ ​0​;​ ​c​u​r​r​e​n​t​R​a​d​i​u​s​ ​-​=​ ​2​0​)​ ​{​

 ​ ​ ​ ​ ​ ​ ​ ​[​p​a​t​h​ ​m​o​v​e​T​o​P​o​i​n​t​:​C​G​P​o​i​n​t​M​a​k​e​(​c​e​n​t​e​r​.​x​ ​+​ ​c​u​r​r​e​n​t​R​a​d​i​u​s​,​ ​c​e​n​t​e​r​.​y​)​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​[​p​a​t​h​ ​a​d​d​A​r​c​W​i​t​h​C​e​n​t​e​r​:​c​e​n​t​e​r​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​a​d​i​u​s​:​c​u​r​r​e​n​t​R​a​d​i​u​s​ ​/​/​ ​n​o​t​e​ ​t​h​i​s​ ​i​s​ ​c​u​r​r​e​n​t​R​a​d​i​u​s​!​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​t​a​r​t​A​n​g​l​e​:​0​.​0​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​n​d​A​n​g​l​e​:​M​_​P​I​ ​*​ ​2​.​0​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​l​o​c​k​w​i​s​e​:​Y​E​S​]​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​C​o​n​f​i​g​u​r​e​ ​l​i​n​e​ ​w​i​d​t​h​ ​t​o​ ​1​0​ ​p​o​i​n​t​s​
 ​ ​ ​ ​p​a​t​h​.​l​i​n​e​W​i​d​t​h​ ​=​ ​1​0​.​0​;​

 ​ ​ ​ ​/​/​ ​D​r​a​w​ ​t​h​e​ ​l​i​n​e​!​
 ​ ​ ​ ​[​p​a​t​h​ ​s​t​r​o​k​e​]​;​
}​

Build and run the application. You should now have concentric circles.

Figure 4.20  BNRHypnosisView drawing concentric circles

BNRHypnosisView drawing concentric circles

You have seen only a sampling of what UIBezierPath can do. Be sure to check out the documentation and try some of the challenges at the end of this chapter to get a better feel for some of the clever things you can do by stringing together arcs, lines, and curves.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset