Handling input from Apple Pencil

Prashant Cholachagudda

After months of contemplation before finally buying an Apple Pencil to go along with the iPad Pro, it turns out it is as magical as they say! This blog post describes how to use Xamarin.iOS and Visual Studio 2017 to build a signature pad app that works with Apple Pencil.

Testing the Apple Pencil Input

While it is possible to use the iOS Simulator to test Apple Pencil input, it’s always best to test on a physical device. There – now you have a reason to upgrade! Start by creating a Single View App in Visual Studio 2017. Open Main.storyboard and drag an Image View to the design surface:

new project

Add New UIImageView

To add a new UIImageView outlet to (filename):

  • Set the Name of the image view to signaturePad
  • Check Enable User Interaction
  • Set the Class to SignatureView
  • Press return.

iOS interprets Apple Pencil input as standard touch inputs but with additional properties, to support writing, add code to handle the touch events:

public override void TouchesMoved(NSSet touches, UIEvent evt)
{
    // 1
    var touch = touches.ToArray().FirstOrDefault();
    if (touch == null)
        return;

    // 2
    UIGraphics.BeginImageContextWithOptions(Bounds.Size, false, 0);
    var context = UIGraphics.GetCurrentContext();
    Image?.Draw(Bounds);

    DrawLine(context, touch);
    Image = UIGraphics.GetImageFromCurrentImageContext();

    //3
    UIGraphics.EndImageContext();
}

This code does the following things:

  1. Gets the touch information from the event; this will be used to draw the line.
  2. Creates a Core Graphics image context that draws the lines on the screen by calling DrawLine method.
  3. Close the context.

The DrawLine methods processes the touch information:

UIColor color = UIColor.Blue;
nfloat width = 4;
nfloat force = 4;

...

void DrawLine(CGContext context, UITouch touch)
{
    //1
    var previousLocation = touch.PreviousLocationInView(this);
    var location = touch.LocationInView(this);

    //2
    context.SetStrokeColor(color.CGColor);
    context.SetLineWidth(width);
    context.SetLineCap(CGLineCap.Round);

    //3
    context.MoveTo(previousLocation.X, previousLocation.Y);
    context.AddLineToPoint(location.X, location.Y);

    //4
    context.StrokePath();
}

This code:

  1. Determines where to draw a line by looking at the previous and current touch locations.
  2. Sets the line properties.
  3. Creates a path.
  4. Draws the line by creating a stroke along the path.

Using the Apple Pencil

At this point, the app is ready to use Apple Pencil. The iPad’s palm rejection prevents your palm from interfering with the writing:

finger input

However, the line’s thickness is uniform no matter the force applied when writing. To draw a thicker line when the user applies greater pressure, use the Force property on the DrawLine method’s touch parameter:

//2
context.SetStrokeColor(color.CGColor);
width = touch.Force > 0 ? touch.Force * force : force;
context.SetLineWidth(width);
context.SetLineCap(CGLineCap.Round);

for_input

Much better now!

If you look at lines closely, you will see that the writing appears slightly notched and has sharp edges and points. This can happen when the Apple Pencil moves quickly on the surface and some of the touch events are lost.

To smooth out the lines, use coalesced touches in TouchesMoved:

// 2
UIGraphics.BeginImageContextWithOptions(Bounds.Size, false, 0);
var context = UIGraphics.GetCurrentContext();
Image?.Draw(Bounds);
foreach (var t in evt?.GetCoalescedTouches(touch))
{
    DrawLine(context, t);
}
Image = UIGraphics.GetImageFromCurrentImageContext();

With this change, the writing looks smooth:

smooth

Wrapping up

It is possible to use UITouch.AltitudeAngle and UITouch.GetAzimuthAngle(View) to further refine the writing. Check out Xamarin’s UITouch documentation and download the complete sample application from Github.

Discuss this post on the Xamarin forums.

0 comments

Discussion is closed.

Feedback usabilla icon