Apple Pencil

PSPDFKit for iOS offers first-class support for Apple Pencil. There are a few places where you can customize how Apple Pencil interacts with PSPDFKit:

Default Behavior

The default flow of using Apple Pencil with PSPDFKit is as follows.

Users can select an annotation tool from AnnotationToolbar. Whenever a touch from Apple Pencil is detected, ApplePencilManager enables Apple Pencil. This can be disabled by setting ApplePencilManager.enableOnDetection to false.

AnnotationStateManager decides whether or not to allow direct (finger) touches to create annotations depending on whether or not Apple Pencil has been enabled according to ApplePencilManager.enabled. This means that after the user starts annotating with Apple Pencil, direct touches stop creating annotations so that the user can scroll and tap with fingers as normal. This can be changed by setting the stylusMode property.

After detecting a touch from Apple Pencil the first time, AnnotationToolbar shows a stylus button that displays the stylus connection status. This can be disabled by setting showsApplePencilButtonAutomatically to false. When this button is tapped, a new ApplePencilController initialized using SDK.shared.applePencilManager is shown, allowing the user to stop using Apple Pencil if they prefer to annotate using fingers.

Apple Pencil Availability

There is no simple way to know whether a device supports Apple Pencil, whether the user has one, and if it is currently connected. All an app knows is that if it receives a touch event of type UITouchTypeStylus, then an Apple Pencil was connected at that time.

PSPDFKit models the availability of Apple Pencil with the ApplePencilManager.detected property. PSPDFKit sets this to true whenever detecting a touch of type UITouchTypeStylus on a page view. If your app detects a touch from Apple Pencil elsewhere, you can set this property so that PSPDFKit can show the stylus button in the annotation toolbar as soon as it appears:

1
2
3
4
let touch: UITouch = ...
if touch.type == .stylus {
    SDK.shared.applePencilManager.detected = true
}
1
2
3
4
UITouch *touch = ...
if (touch.type == UITouchTypeStylus) {
    PSPDFKitGlobal.sharedInstance.applePencilManager.detected = YES;
}

Every time this property is set, the ApplePencilManager class posts PSPDFApplePencilDetectedNotification. Note that this happens even if the value does not change.

To only show a UI element if Apple Pencil is definitely available so that it does not clutter the screen otherwise, use the following:

Copy
1
2
3
4
5
6
7
func someSetupMethod() {
    NotificationCenter.default.addObserver(self, selector: #selector(stylusDetectionChanged(notification:)), name: .PSPDFApplePencilDetected, object: ApplePencilManager.self)
}

@objc func stylusDetectionChanged(notification: Notification) {
    self.showsSomeStylusUI = SDK.shared.applePencilManager.detected
}
Copy
1
2
3
4
5
6
7
- (void)someSetupMethod {
    [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(stylusDetectionChanged:) name:PSPDFApplePencilDetectedNotification object:PSPDFApplePencilManager.class];
}

- (void)stylusDetectionChanged:(NSNotification *)notification {
   self.showsSomeStylusUI = PSPDFKitGlobal.sharedInstance.applePencilManager.detected;
}

Making Apple Pencil Available Across Your Application

To get the absolute best entry point for automatic Apple Pencil detection, subclass UIApplication and override sendEvent:

Copy
1
2
3
4
5
6
7
8
9
10
11
12
class Application: UIApplication {
    override func sendEvent(_ event: UIEvent) {
        super.sendEvent(event)
        let pencilManager = SDK.shared.applePencilManager
        guard applePencilManager.detected == false, event.type == .touches, let touches = event.allTouches else {
            return
        }
        if (touches.contains { $0.type == .stylus && $0.phase == .began }) {
            applePencilManager.detected = true
        }
    }
}

Then make sure that the application is loaded from UIKit when it starts up via writing main.swift:

1
UIApplicationMain(CommandLine.argc, CommandLine.unsafeArgv, NSStringFromClass(Application.self), NSStringFromClass(AppDelegate.self))

The syntax for subclassing UIApplication in Objective-C is slightly different, but accomplishing this is also possible.

Always Create a Particular Annotation Type with Apple Pencil

You can ensure touches with Apple Pencil always create a particular annotation type, which would be a good fit if your app does not show the annotation toolbar and focuses on one type of annotation. For example, you could set Apple Pencil touches to always draw ink. Alternatively, you could set Apple Pencil to always highlight text similar to how it’s done in Books, which you can see in the Books-like highlighting example, (PSCBooksHighlightingExample).

First set up the annotation state manager:

Copy
1
2
3
let annotationStateManager = pdfController.annotationStateManager
annotationStateManager.state = .ink
annotationStateManager.stylusMode = .stylus
Copy
1
2
3
PSPDFAnnotationStateManager *annotationStateManager = pdfController.annotationStateManager;
annotationStateManager.state = PSPDFAnnotationStringInk;
annotationStateManager.stylusMode = PSPDFAnnotationStateManagerStylusModeStylus;

Make sure the user can’t change the state by disabling the annotation toolbar by removing the annotationButtonItem, which is included by default, and disabling the menu shown on long press:

Copy
1
2
3
4
pdfController.navigationItem.setRightBarButtonItems([pdfController.thumbnailsButtonItem, pdfController.activityButtonItem /* etc. as long as this excludes annotationButtonItem */], for: .document, animated: false)
pdfController.updateConfiguration { builder in
    builder.isCreateAnnotationMenuEnabled = false
}
Copy
1
2
3
4
[pdfController.navigationItem setRightBarButtonItems:@[pdfController.thumbnailsButtonItem, pdfController.activityButtonItem /* etc. as long as this excludes annotationButtonItem */] forViewMode:PSPDFViewModeDocument animated:NO];
[pdfController updateConfigurationWithBuilder:^(PSPDFConfigurationBuilder *builder) {
    builder.createAnnotationMenuEnabled = NO;
}];