4.3 Interaction With CoreGraphics
CoreGraphics is a powerful framework. In this lesson, you'll learn how to use it to interactively draw on the screen.
1.Introduction3 lessons, 12:12
2.Animation3 lessons, 21:28
3.Networking3 lessons, 23:55
4.Custom Controls3 lessons, 32:22
5.Conclusion1 lesson, 01:44
4.3 Interaction With CoreGraphics
Hi and welcome back to Go Further with Swift where we built a weather app for iOS. In this lesson, I'll be doing something that has much to do with the weather but is a fun way to try out core graphics and also handle into action by the user. If you want to draw something on iOS, shape layers aren't your only option. To try this, I'm going to add a new view on top and also spread it over the whole screen. I'm also adding a new Swift Plus that is going to represent this view and name it DrawingCanvas. It will do exactly what it says. We are going to let the user draw on the screen. I'm going to make this an IBDesignable class inheriting from UFU as well. Even though you won't see much in interface buildup, but you will be able to change some settings. The first parameter is going to be stroke with type CGFloat. It defaults to a size of five. The second one is representing the UI color and defaults to white. When I changed the class of the view we just added an interface builder, those two options appear in the inspector. Back in the Canvas class, we're going to add the interactive part of the class first. Whenever the user moves their finger around the screen we want to draw a line. UIKit provides a function called touchesMoved, that tracks the movement of fingers on the view object. I'm not going to add support for multiple touches but I'm challenging you to implement this functionality as an exercise. Therefore I can simply get the first value of the touches array and unwrap it. Next we're calling the draw function addLine, as we'll create in a minute. With a from point that is the previous location of the touch which is conveniently provided to us as well as the current location of the touch as the end point. To get the correct coordinates for this method we need to transform them into the local coordinate system of our view. Which can be different from the global coordinates if, for instance the view itself doesn't lie in the origin of the screen. Now to the addLine function. It takes two parameters of type CGPoint, that represent the coordinates of the line. Now it is time to draw. The graphics uses context as a way to configure your canvas. This API is very C-like which means you need to match each call of begin to a call to end. In our case we're using UI graphics BeginImage context with options to create a new image context. We also need to specify the size of this context as well as the opaque property. Since the view should show everything beneath it, meaning it's transparent, this option needs to be set to False. The final property is the scale. We don't use it here, so I'm setting it to 0. Notice that I'm already calling UI graphics and image context. Since I don't want to forget about it down the road, those calls can also be nested. It is good to not lose track. There will be many calls of headline over the cause of a touch. Each time we need to store the end results somewhere. That's why I'm creating another property on the class that stores an optional UI image. The first step of drawing is to draw the current result to the context. This is done by using a function called Draw on the UI image and use a CGRect as a parameter, which is going to be the bounds. Since I'm calling this with a question mark, it won't be called if canvas is nil. This saves some lines of unwrapping the canvas. Now it's time to draw the new line. First, we're going to get the currently stored graphics context. This system acts like a stack. You can push and pull from it to nest your context. Then we have to move the cursor of the context to the starting point, which is the fromPoint in our case. The next step is to add a line from our current cursor position to the toPoint, which also moves the cursor there. We already used this cursor analogy with Shapes but it is an important concept to understand. We could think of it like a brush that gets moved around a canvas to draw something. Up until now we actually didn't draw anything. We added some instructions to the current context to execute later on. This is why we can change the context settings after we cue the drawing commands. In our case we want to set the line cap to round so the strokes look more natural. We can also set the line width on the context. With colors it is a bit different. We set colors on the current context by calling functions on the color itself. Black set stroke. Finally, it is time to issue the drawing command. Well this will work through all the drawing commands and draw on the image context. To fetch the new image from the current image context we can use another method. UI graphics get image from current image context. Then we have to set a context of the fuse layer to be the image, CG image reference. To recap we create a new image content, draw the previous image, draw the line with another context, save the image, and set the new image on the fuse layer. Let's try it out. I'm using my mouse to draw on the screen and it looks really good. I can also stop and start from another point. What you have learned here is that you can do something very advanced with just a few lines of code. For graphics, seems scary if you read about it, but it's actually very neat and can also be very simple. Don't get me wrong, it can get complicated but you don't need to fear this framework.