Smooth Freehand Drawing with Arrow in iOS

 

Hello, folks.

In this post we are going to implement a reusable component for freehand drawing. We will see how to use UIGestureRecognizer for drawing and using linear polarization to make our drawing smooth. We will also create a method to draw an arrow with respect to two points.

So let’s start with creating a simple UIView subclass on which we will draw. Just create the class LineHolderView and add following code:

 

What we did here, we created a subclass of UIView and in both initializers called a method initializeView().

In initializeView() method we disabled the multiple touch, create an object of UIBezierPath class and added and UIPanGestureRecognizer at the view. Here you can change the thickness of drawing at line 32.

Now let’s see the implementation of method viewDragged(_: ) which is the selector for UIPanGestureRecognizer and will be called when user drags on the view.

So in this first we get the current location of user’s finger in view and if the location if outside of the container then we ignore the drawing. Now the state property of UIPanGestureRecognizer tells us whether the drag is started, changed or ended. So we have Added a switch case on state property and did following of different states:

  • Began: When user began the dragging we move our bezier path to that point.
  • Changed: When drag changed we added an line to the current point.
  • Ended: In this we used the method saveBufferImage() to convert the current drawing into an image and store it. It will save the cost of redrawing whole path next time.

In draw(_:) method we first draw the stored buffer image and then  we set the stroke color for line color and stroke the bezier path. You can change the stroke color if you want to change the line color.

That’s it, set this LineHolderView as a class to any view in storyboard and done, start drawing.

Here is the result:

freehand-drawing-1

As you can see in the above image the lines have sharp changes in it. So here we a concept named ‘Linear Polarization’ to smoother our drawing. In Linear polarization we draw an arc with four points and two of them works as control points and the arc drawn below the tangent of first and second point and tangent of third and fourth points.

Here are some examples of arcs drawn with two control points:

bezier

 

interestingshapes

So here we will use an array to store the 4 points, and when we get four points then we will draw an arc between them with two control points using the UIBezierPath method addCurve(to endPoint: CGPoint, controlPoint1: CGPoint, controlPoint2: CGPoint).

Here is the complete code after changes:

 

Compile and Run it. You will get some output like this:

 

freehand-drawing-2

As you can see the drawing is improved but it have some problem when there is a mismatch in the tangents of last and starting point of the curve as shown in following picture.

mismatched

So now we have to match the tangents by calculating a new intersection point between two curves like following picture:

shiftingjunctionpoint

So we have to calculate a middle point of 3rd point of first curve and 1st point of second curve. So we will need an extra point to calculate the midpoint. Now the points array will have 5 points.

Change the class with following code and run.

 

Compile and Run it. You will get some output like this:

freehand-drawing-3

Now we have a smooth drawing.

Create Arrow

Now, lets create an arrow head at the end of the line segment. for this we will use last two points to calculate three points to create an arrow.

To calculate these points we will use following steps.

  1. First we will convert this line segment(made by last two points) into unit vector and translate this unit vector to origin.
    1. calculate normal

      • Freehand Drawing 1
    2. calculate unit vector

      • Freehand Drawing 2
  2. Now rotate this point (udx, udy) by 150° clockwise and anti-clockwise.

    • Freehand Drawing 3
  3. Now we have two points (ax, ay) and (bx, by). Lets translate these points to end of the line segment to get the points for our arrow.

    • Freehand Drawing 4
  4. Till now this arrow if of unit length, to get a proper length scale these points with and scaling factor(s).

    • Freehand Drawing 5
  5. Now combine these points (ax0, ay0), (x2, y2) and (bx0, by0) to create a line segment for arrow.

 

The complete implementation of the algorithm is given below.

 

Use this method to get three points and draw line with these three lines in your draw(_:) method.

Thanks for reading. Hope you like the post. 🙂

Leave a Reply