Ios Draw Circle in View
Update notation: Michael Katz updated this tutorial for iOS fourteen, Xcode 12 and Swift 5. Ray Wenderlich wrote the original.
Cadre Graphics is a powerful, yet friendly, set of APIs for drawing in a UIKit application. In add-on to primitives like shapes and gradients, with Core Graphics yous can code upwards patterns. Core Graphics Patterns are an arbitrary sets of graphics operations that can be tiled to make full an surface area. You can create magnificent backgrounds for your apps using repeated shapes. Core Graphics Patterns are a performant mode to scale a drawing to fill up whatever shape on the screen.
In this tutorial, you'll learn how to use Core Graphics to exercise the following:
- Create a path-based drawing.
- Draw a pattern.
- Transform the pattern.
- Utilize patterns to finish a design recognition game called Recall.
Note: If you're make new to Core Graphics, it would exist a good idea to review some of our entry-level tutorials on the topic. Consider working through the Lines, Rectangles, and Gradients and Arcs and Paths tutorials to get a better understanding of the foundations you'll build upon hither.
Getting Started
Commencement by clicking the Download Materials button at the peak or bottom of this tutorial. Build and run the starter app. Y'all'll see this (colors and letters may vary):
Recall takes inspiration from a game in the Left vs Right brain preparation app. The goal of the game is to choose the most popular direction for objects in view. A new fix of objects displays in one case you make a option. You have 5 attempts before the game ends.
Recall groups pattern objects in four quadrants. Each quadrant in the starter app has a label. The text represents the direction and the background color represents the fill colour.
As a starting signal, the game is rather underwhelming. :[
Your chore is to use Cadre Graphics patterns to plow this pitiful app into the finished app below:
Look at the projection in Xcode. These are the main files:
- GameViewController.swift: Controls the gameplay and displays the game view.
- ResultViewController.swift: Displays the terminal score and a button to restart the game.
- PatternView.swift: Displays the design view for one of the quadrants in the game view.
Toward the end of this tutorial, you'll raise PatternView
to display the desired pattern.
For starters, you lot'll prototype your new and improved PatternView
in a Playground. This allows yous to iterate much faster while learning the ins and outs of Core Graphics patterns. Once you're done, you'll transfer the relevant lawmaking over to the Remember starter project.
In Xcode, go to File ▸ New ▸ Playground…. Select the Single View template. Click Next, name the playground PatternView.playground, add it to the Recollect workspace and the Recall binder group and click Create. Your playground contains a view controller with a single view.
Note: If Xcode gives you lot an error trying to load the new program, the historic period-old "restart Xcode" should get you on your way.
Select Editor ▸ Run Playground to execute the playground. Click Show the Assistant Editor to display your starter view. It displays the iconic "How-do-you-do World!" upshot:
As you go through the side by side sections, you'll replace the starter view with your design view. Time to get started!
Agreement the Beefcake of a Pattern
In our previous Core Graphics tutorials, you've seen how to define and paint paths similar this:
A path is a set of instructions describing a shape. The example above shows you stroking the path on the left with a black line. The path on the right has been stroked with black and filled in with orange.
Using Core Graphics, you tin also stroke or fill a path with a blueprint. The case below shows a colored pattern filling out a path:
Setting up the Pattern
You set up a pattern by doing the following:
- Write a method that draws an individual design jail cell.
- Create a pattern with parameters that include how to describe and place an individual cell.
- Define the color information that your pattern will use.
- Paint the desired path with the blueprint y'all created.
Now, check out a slightly dissimilar blueprint cell with extra padding. The thin blackness border shows the bounds of the cell:
You volition write a describe method that draws within the bounds of the cell. Core Graphics clips anything drawn outside the prison cell bounds. Core Graphics besides expects you to draw the pattern prison cell exactly the same way each time.
Your draw method tin can employ color when setting up your blueprint cell. This is a colored pattern. An uncolored or masking pattern is 1 where you apply the fill colour outside of the draw method. This gives you the flexibility to prepare up your blueprint colors where information technology makes sense.
Core Graphics calls your draw method repeatedly to gear up your blueprint. The pattern creation parameters define what the blueprint looks like. The example below shows a basic repeated pattern with the cells lining up correct next to each other:
You can specify the spacing between the pattern cells when you configure the pattern:
Y'all can as well apply transformations to change the pattern's advent. The pictures beneath testify the pattern drawn inside a space represented past the fuzzy edge:
The first shows an unchanged pattern. In the second, you meet a translated blueprint. The third shows the pattern rotated. Again, the blackness border around the pattern cells highlights its bounds.
You take a lot of options bachelor when configuring a blueprint. You lot'll start putting all this together in the next department.
Creating the Pattern View
Add the post-obit code earlier the view controller class in PatternView.playground:
course PatternView: UIView { override func draw(_ rect: CGRect) { // i guard let context = UIGraphicsGetCurrentContext() else { return } // 2 UIColor.orange.setFill() // 3 context.fill(rect) } }
This represents the custom view for your design. Here, you override draw(_:)
to practise the following:
- Get the view's graphics context.
- Fix the electric current fill color for the context.
- Fill the unabridged context with the current make full colour.
Think of the graphics context as a sheet you can draw on. The context contains data such as the color that will make full in or stroke a path. Yous can describe out paths in your canvas before painting them in with a context's colour data.
Inside MyViewController
, replace the code in loadView()
related to label
with the post-obit:
allow patternView = PatternView() patternView.frame = CGRect(x: x, y: ten, width: 200, height: 200) view.addSubview(patternView)
This creates an instance of the design view, sets its frame and adds information technology to view
.
Press Shift-Control-Return to run the playground. The previous label is gone and replaced with an orangish subview:
Coloring is just the first of the journey. You know in that location's more than where that came from!
Cartoon a Blackness Circumvolve in the Pattern Prison cell
Add the following property inside the top of PatternView
:
let drawPattern: CGPatternDrawPatternCallback = { _, context in context.addArc( middle: CGPoint(x: 20, y: xx), radius: 10.0, startAngle: 0, endAngle: two.0 * .pi, clockwise: false) context.setFillColor(UIColor.black.cgColor) context.fillPath() }
The lawmaking above draws a circular path in the graphics context and fills it with black color.
This represents your blueprint prison cell'south drawing method, which is of the type CGPatternDrawPatternCallback
. This is a closure with two arguments:
- A pointer to individual data associated with the pattern. You lot're not using individual information, and so you use an unnamed parameter here.
- The graphics
context
used in drawing your pattern cell.
Add together the post-obit code to the end of describe(_:)
:
var callbacks = CGPatternCallbacks( version: 0, drawPattern: drawPattern, releaseInfo: nil)
CGPatternCallbacks
is a structure for belongings the callbacks used to draw a pattern. There are ii types of callback — one for the design, which you've made already, and one for cleaning upwardly and releasing any individual data, which you aren't using. You would typically prepare upwardly a release callback if you're using private data in the pattern. Since you don't apply individual data in your draw method, yous pass nil
for this callback.
Creating the Pattern
To create the pattern, add the following correct after the code above:
guard let blueprint = CGPattern( info: naught, bounds: CGRect(x: 0, y: 0, width: 20, height: 20), matrix: .identity, xStep: 50, yStep: fifty, tiling: .constantSpacing, isColored: truthful, callbacks: &callbacks) else { return }
This creates a pattern object. In the code to a higher place, y'all pass in the following parameters:
- info: A pointer to whatsoever individual data you want to use in your pattern callbacks. You pass in
nil
here since you're not using any. - bounds: The pattern cell'south bounding box.
- matrix: A matrix that represents the transform to apply. Y'all pass in the identity matrix, as you're non applying any transforms.
- xStep: The horizontal spacing between pattern cells.
- yStep: The vertical spacing betwixt blueprint cells.
- tiling: The technique Cadre Graphics should use to business relationship for differences between user space units and device pixels.
- isColored: Whether the blueprint prison cell draw method applies colour. You're setting this to
truthful
since your draw method sets a color. - callbacks: A pointer to the construction that holds the pattern callbacks.
Add the following lawmaking right after the blueprint
assignment:
var alpha: CGFloat = 1.0 context.setFillPattern(design, colorComponents: &alpha) context.fill(rect)
The code above sets the fill up pattern for the graphics context. For colored patterns, you must too pass in an alpha value to specify the pattern opacity. The pattern draw method provides the color. Finally, the code paints the view's frame area with the design.
Run the playground past pressing Shift-Command-Render. Your pattern isn't showing up. Foreign. What'south going on?
Setting the Blueprint'south Colour Infinite
You lot need to provide Core Graphics with information about your pattern'due south color space then it knows how to handle pattern colors. Colour space specifies how to interpret a colour value for brandish.
Add together the post-obit earlier the alpha
declaration:
// 1 guard permit patternSpace = CGColorSpace(patternBaseSpace: nil) else { render } // 2 context.setFillColorSpace(patternSpace)
Here'south what the code does:
- Create a pattern color space. The base space parameter should be
nil
for colored patterns. This delegates the coloring to your pattern jail cell draw method. - Set the fill color space to your divers blueprint color space.
Run the playground. Yes! You should now encounter a circular blackness pattern:
Side by side, you'll learn more about configuring patterns.
Configuring the Design
In your playground, change the spacing parameters that set upwards design
as follows:
xStep: 30, yStep: 30,
Run the playground. Note that the circular dots appear to be much closer to each other:
This makes sense, as y'all've shrunk the stride size between pattern cells.
Now, change the spacing parameters as follows:
xStep: 20, yStep: 20,
Run the playground. Your circles have turned into quarters:
Irresolute the Centers of the Circles
To empathise why, note that your draw method returns a circle with a radius of x centered at (twenty, 20). The blueprint's horizontal and vertical displacement is twenty. The cell's bounding box is 20×xx at origin (0,0). This results in a repeating quarter-circle that starts at the lower right border.
Change drawPattern
that draws the circumvolve (context.addArc
) to the post-obit:
context.addArc( heart: CGPoint(10: 10, y: x), radius: 10.0, startAngle: 0, endAngle: 2.0 * .pi, clockwise: false)
You've changed the eye point to be (ten, 10) rather than (twenty, 20).
Run the playground. You're back to whole circles due to the shift in the circle's middle:
The pattern jail cell bounds as well match up perfectly with the circumvolve, resulting in each cell abutting the others.
Applying a Transformation to the Design
Y'all tin can transform your pattern in many interesting ways. Inside describe(_:)
, replace pattern
with the following:
// 1 allow transform = CGAffineTransform(translationX: 5, y: 5) // 2 guard allow pattern = CGPattern( info: nil, premises: CGRect(x: 0, y: 0, width: 20, height: 20), matrix: transform, xStep: 20, yStep: 20, tiling: .constantSpacing, isColored: true, callbacks: &callbacks) else { return }
You've modified the design by passing it a transformation matrix. Here's a closer look:
- Create an affine transformation matrix that represents a translation.
- Configure the pattern to use this transformation past passing it in
matrix
.
Run the playground. Note how the blueprint shifted to the right and downward to match the translation y'all divers:
Besides translating your pattern, you tin also scale and rotate the pattern cells. You'll encounter how to rotate the pattern later when yous build the pattern for the game app.
Adding Fill and Stroke to the Pattern
Here's how to fill up and stroke a colored pattern. In drawPattern
replace the lines where you set the make full colour and fill up the path with the following:
context.setFillColor(UIColor.yellow.cgColor) context.setStrokeColor(UIColor.darkGray.cgColor) context.drawPath(using: .fillStroke)
Here, you change the fill up color to yellow and fix the stroke color. You then call drawPath(using:)
with the option that fills and strokes the path.
Run your playground and check that the pattern now shows your new fill color and stroke:
Thus far, you've worked with colored patterns and defined the colors in the pattern describe method. In the finished game, you'll accept to create patterns with different colors. You probably realize that writing a depict method for each color isn't the style to go. This is where masking patterns come up into play.
Using Masking Patterns
Masking patterns define their color information outside the pattern cell draw method. This allows you to alter upward the pattern color to suit your needs.
Here's an example of a masking pattern that has no color associated with information technology:
With the blueprint in place, you lot can at present apply color. The commencement example below shows a blue color applied to the mask, and the second displays an orange colour:
Now, you'll change the pattern yous've been working with to a masking pattern.
Delete the code from drawPattern
where you set fill and stroke colors and draw the path and replace information technology with the following:
context.fillPath()
This reverts the code back to filling the path.
Replace pattern
with the post-obit:
guard allow pattern = CGPattern( info: nil, bounds: CGRect(10: 0, y: 0, width: twenty, summit: xx), matrix: transform, xStep: 25, yStep: 25, tiling: .constantSpacing, isColored: false, callbacks: &callbacks) else { return }
This sets isColored
to false
, changing your pattern to a masking pattern. You've also increased the vertical and horizontal spacing to 25. Now, you lot need to provide the color space information for your pattern.
Replace the existing patternSpace
consignment with the following:
let baseSpace = CGColorSpaceCreateDeviceRGB() baby-sit let patternSpace = CGColorSpace(patternBaseSpace: baseSpace) else { return }
Here, you get a reference to a standard device-dependent RGB color infinite. You and so change your pattern color infinite to this value instead of the previous nil
value.
Beneath that, supervene upon the lines where you create blastoff
and prepare the context make full blueprint with the post-obit:
let fillColor: [CGFloat] = [0.0, 1.0, 1.0, 1.0] context.setFillPattern(pattern, colorComponents: fillColor)
This creates a color applied underneath the mask when filling out the design.
Run the playground. Your pattern'south color updates to reflect the cyan color setting that you configured outside the depict method:
Stroking and Filling the Masking Pattern
Now, it's time to stroke and fill a masking design. It's like stroking a colored pattern.
Supersede the line context.fillPath()
in drawPattern
with the following:
context.setStrokeColor(UIColor.darkGray.cgColor) context.drawPath(using: .fillStroke)
Although you gear up the stroke colour within draw(_:)
, your blueprint color is still set up outside the method.
Run the playground to run into the stroked pattern:
You've now congenital upward experience with dissimilar pattern configurations and with masking patterns. You lot can begin building out the pattern yous'll need for Recall.
Creating the Game Blueprint
Add the following lawmaking to the top of the playground:
extension CGPath { // 1 static func triangle(in rect: CGRect) -> CGPath { allow path = CGMutablePath() // 2 let acme = CGPoint(x: rect.width / 2, y: 0) let bottomLeft = CGPoint(10: 0, y: rect.height) let bottomRight = CGPoint(x: rect.width, y: rect.height) // 3 path.addLines(betwixt: [top, bottomLeft, bottomRight]) // iv path.closeSubpath() render path } }
Going through the code, step by step:
- Extend
CGPath
to create a triangular path. - Specify the three points that brand up the triangle.
- Add lines between the points.
- Close the path.
Then inside PatternView
, add the following empty enum:
enum Constants { static let patternSize: CGFloat = 30.0 static permit patternRepeatCount: CGFloat = 2 }
These represent the constants you lot'll use when setting up your pattern. patternSize
defines the pattern cell size, and patternRepeatCount
defines the number of pattern cells in the pattern view.
Drawing a Triangle
Add the following after the Constants
definition:
let drawTriangle: CGPatternDrawPatternCallback = { _, context in let trianglePath = CGPath.triangle(in: CGRect( x: 0, y: 0, width: Constants.patternSize, height: Constants.patternSize)) context.addPath(trianglePath) context.fillPath() }
This defines a new callback for drawing your triangular pattern. In it, you call CGPath.triangle(in:)
to return a path representing the triangle. Then you add together this path to the context before filling it.
Annotation that the closure doesn't specify a fill colour, so it tin can be a masking pattern.
In depict(_:)
, change callbacks
to the post-obit:
var callbacks = CGPatternCallbacks( version: 0, drawPattern: drawTriangle, releaseInfo: nil)
You lot're now using the triangle drawing callback.
Drawing Repeating Triangles as a Pattern
Delete drawPattern
, every bit it's no longer necessary. One can just go effectually in circles for so long. :]
Also, in draw(_:)
, replace the lawmaking that assigns transform
and pattern
with the following:
// i let patternStepX = rect.width / Constants.patternRepeatCount let patternStepY = rect.height / Constants.patternRepeatCount // two let patternOffsetX = (patternStepX - Constants.patternSize) / 2.0 allow patternOffsetY = (patternStepY - Constants.patternSize) / 2.0 // 3 let transform = CGAffineTransform( translationX: patternOffsetX, y: patternOffsetY) // four baby-sit let pattern = CGPattern( info: nil, bounds: CGRect( x: 0, y: 0, width: Constants.patternSize, height: Constants.patternSize), matrix: transform, xStep: patternStepX, yStep: patternStepY, tiling: .constantSpacing, isColored: false, callbacks: &callbacks) else { return }
Here'southward what that code does, step by step:
- Calculate the horizontal and vertical footstep size using the view'south width and acme, every bit well as the number of pattern cells in a view.
- Work out the dimensions to horizontally and vertically heart a pattern cell within its premises.
- Ready up a
CGAffineTransform
translation based on the centering variables you defined. - Create the pattern object based on your calculated parameters.
Run the playground. You lot volition run across four triangles, each centered both vertically and horizontally within their bounds:
You lot'll side by side go your background colors to more than closely lucifer the Recall app.
In MyViewController
, modify the groundwork color setup in loadView()
as follows:
view.backgroundColor = .lightGray
Next, go to PatternView
and change the context make full setup in draw(_:)
as follows:
UIColor.white.setFill()
Run the playground. Your principal view's background should now be grey with a white background for your pattern view:
Customizing the Pattern View
At present that you have the basic pattern displaying correctly, y'all can make changes to control the pattern direction.
Add the following enumeration about the summit of PatternView
after Constants
:
enum PatternDirection: CaseIterable { example left case top case right instance bottom }
This represents the different directions the triangle can point. They match the directions in your starter app.
Add the following properties to PatternView
:
var fillColor: [CGFloat] = [one.0, 0.0, 0.0, ane.0] var direction: PatternDirection = .top
This represents the color you'll apply to the masking pattern and the design direction. The class sets a default color of red (the iv array components represent red, green, blueish and blastoff) and the default direction of peak.
Delete the local fillColor
declaration plant about the lesser of depict(_:)
. This will ensure that you employ the case property instead.
Supercede transform
with the post-obit:
// 1 var transform: CGAffineTransform // 2 switch direction { case .top: transform = .identity instance .right: transform = CGAffineTransform(rotationAngle: 0.5 * .pi) case .bottom: transform = CGAffineTransform(rotationAngle: .pi) case .left: transform = CGAffineTransform(rotationAngle: 1.5 * .pi) } // 3 transform = transform.translatedBy(10: patternOffsetX, y: patternOffsetY)
Hither's what just happened:
- Declare a
CGAffineTransform
variable for your blueprint transform. - Assign the transform to the identity matrix if the pattern management is
top
. Otherwise, the transform is a rotation based on the direction. For example, if the pattern points right, then the rotation is π / 2 radians or 90º clockwise. - Apply a
CGAffineTransform
translation to center the pattern jail cell within its bounds.
Run the playground. Your triangles are red, based on your default pattern fill color:
Changing the Pattern'south Color and Direction
Now's a corking time to set up upwards code to command and examination the design colour and direction.
Add the post-obit methods in PatternView
subsequently the property definitions:
init(fillColor: [CGFloat], management: PatternDirection = .tiptop) { self.fillColor = fillColor self.direction = direction super.init(frame: CGRect.nil) } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
This sets up an initializer that takes in a fill color and a pattern management. direction
has a default value.
You've also added the required initializer for when a storyboard initializes the view. You'll need this later when yous transfer your code to the app.
In MyViewController
, change patternView
since you've changed the initializer:
let patternView = PatternView( fillColor: [0.0, one.0, 0.0, i.0], management: .right)
Here, you instantiate a pattern view with not-default values.
Run the playground. Your triangles are at present green and pointing to the correct:
Congratulations! You're washed prototyping your blueprint using Playgrounds. It's time to utilize this blueprint in Call up.
Updating the Game to Use the Pattern
Open up the Retrieve starter projection. Become to PatternView.swift and copy over the CGPath
extension from your playground to the end of the file.
Next, supervene upon PatternView
in PatternView.swift with the class from your playground.
Note: You lot can vastly simplify this procedure by using Xcode'due south code folding feature. In the playground, put the cursor just after the opening caryatid of class PatternView: UIView {
and select Editor ▸ Code Folding ▸ Fold from the card. Triple-click the resulting folded line to select the unabridged course and printing Command-C. In the project, echo the process to fold and select the course. Press Control-Five to replace it.
Build and run the app. You lot should see something like this:
Something'due south non quite right. Your pattern appears to be stuck in default mode. It looks like a new game view isn't refreshing the pattern views.
Go to GameViewController.swift and add together the following to the end of setupPatternView(_:towards:havingColor:)
:
patternView.setNeedsDisplay()
This prompts the system to redraw the pattern so that it picks upward the new pattern data.
Build and run the app. Y'all should at present run into colors and directions nicely mixed up:
Tap one of the respond buttons and play through the game to cheque that everything works as expected.
Congratulations on completing Recall! Yous've come a long fashion from the mind-numbing days of simple pigment jobs.
Considering Performance When Using Patterns
Core Graphics patterns are really fast. Here are several options you can use to describe patterns:
- Using the Core Graphics design APIs that you worked through in this tutorial.
- Using UIKit wrapper methods such as
UIColor(patternImage:)
. - Drawing the desired pattern in a loop with many Core Graphics calls.
If your pattern is but fatigued once, the UIKit wrapper method is the easiest. Its performance should also be comparable to the lower-level Core Graphics calls. An instance of when to utilize this is to pigment a static groundwork pattern.
Core Graphics can work in a groundwork thread, unlike UIKit, which runs on the chief thread. Core Graphics patterns are more performant with complicated drawings or dynamic patterns.
Drawing the pattern in a loop is the slowest pick. A Core Graphics blueprint makes the draw call once and caches the results, making information technology more efficient.
Where to Go From Here?
Access the final project by clicking the Download Materials push at the top or bottom of this tutorial. Y'all can besides use this to cheque your piece of work if you get stuck.
Yous should at present have a solid agreement of how to use Core Graphics to create patterns. Check out the Patterns and Playgrounds tutorial to learn how to build patterns using UIKit.
You may too want to read through the Curves and Layers tutorial, which focuses on drawing various curves and cloning them using layers.
Nosotros hope you enjoyed this tutorial. If you take any comments or questions, please join the forum give-and-take below!
Source: https://www.raywenderlich.com/21462527-core-graphics-tutorial-patterns
0 Response to "Ios Draw Circle in View"
Enviar um comentário