Apple Pencil Proのスクイーズに対応する

# はじめに

[Apple Pencil Pro](https://www.apple.com/jp/shop/product/MX2D3ZA/A/apple-pencil-pro)が2024年5月に発売され、ペンの操作に新しいアクション: **スクイーズ** が追加されました。
スクイーズ(squeeze)とは、「強く押す」といった意味などがある単語です。Apple Pencil Pro対応の新しいAPI[^newapi]を使うことで、ペンをギュッと握ったときのアクションを実装することができます。

本稿では、スクイーズの実装例を僕が個人開発している手書きノートアプリ[^bluesketch]から一部抜粋して紹介します。

# スクイーズ時のアクション

Apple Pencil Proをスクイーズしたときのアクションは 設定 > Apple Pencil で選択することができます。

![ApplePencilPro_settings.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73490/5908fa49-52f0-8956-ad05-3be34aeedbd5.png)

このアクションは[UIPencilPreferredAction](https://developer.apple.com/documentation/uikit/uipencilpreferredaction)と対応しています。

|アクション|UIPencilPreferredAction|
|:-:|:-:|
|ツールパレットを表示|showContextualPalette|
|現在使用中のツールと消しゴムの切り替え|switchEraser|
|現在使用中と前回使用したツールの切り替え|switchPrevious|
|カラーパレットを表示|showColorPalette|
|インクの属性を表示|showInkAttributes|
|ショートカット|runSystemShortcut|
|オフ|ignore|

ユーザが設定しているアクションは 

```swift
let preferredAction = UIPencilInteraction.preferredSqueezeAction
```

で取得できます。スクイーズ機能を実装する場合、このAPIが返すアクションに対応した（近しい）機能を提供すると良いです。

# 実装

まずはじめに `UIPencilInteraction` を登録します。

```swift:ViewController.swift
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        let pencilInteraction = UIPencilInteraction()
        pencilInteraction.delegate = self
        view.addInteraction(pencilInteraction)
    }
}
```

次に `UIPencilInteractionDelegate` を実装します。Apple Pencil Proをスクイーズしたとき、  `pencilInteraction(_:,didReceiveSqueeze:)` メソッドが呼ばれます。

```swift:ViewController.swift
extension ViewController: UIPencilInteractionDelegate {
    @available(iOS 17.5, *)
    func pencilInteraction(_ interaction: UIPencilInteraction,
                           didReceiveSqueeze squeeze: UIPencilInteraction.Squeeze)
    {
        // Apple Pencil Proをスクイーズしたらユーザが設定しているアクションに合わせてツールを切り替える
        let preferredAction = UIPencilInteraction.preferredSqueezeAction

        // ユーザが設定しているアクションがオフのときは何もしない
        guard preferredAction != .ignore else { return }

        if squeeze.phase == .ended {
            switch preferredAction {
                case .switchEraser,
                     .runSystemShortcut,
                     .showColorPalette,
                     .showContextualPalette,
                     .showInkAttributes:
                    if model.currentTool == .eraser {
                        // 消しゴムなら前回使用したツールに戻す
                        model.setTool(model.previousTool)
                    } else {
                        // 消しゴムに切り替え
                        model.setTool(.eraser)
                    }
                case .switchPrevious:
                    // 前回使用したツールに切り替え
                    model.setTool(model.previousTool)
                default:
                    break
            }
        }
    }
}
```

僕が開発しているアプリでは、 `UIPencilPreferredAction` に対応している機能は `switchEraser` と `switchPrevious` くらいのため、その他のアクションは `ignore` 以外は消しゴムとの切り替えに割り振っています。

`squeeze: UIPencilInteraction.Squeeze` にはApple Pencil Proの位置を返すプロパティもあります。

```swift
if let anchorPoint = squeeze.hoverPose?.location {
    // Apple Pencilの近くにUIを表示するなど
}
```

この `location` からApple Pencil Proの近くにカラーパレットを表示するといったこともできます。

# まとめ

スクイーズの検知は非常に簡単に実装することができます。
あとはアクションにあわせた機能やUIを作り込むことができれば、よりリッチなアプリになることでしょう。

# 参考

Apple公式ドキュメント

https://developer.apple.com/documentation/applepencil/handling-squeezes-from-apple-pencil

[^newapi]:Apple Pencil Pro対応のAPIはiOS 17.5から使えます。https://developer.apple.com/documentation/uikit/uipencilinteractiondelegate/4391665-pencilinteraction

[^bluesketch]:Blue Sketch https://apps.apple.com/jp/app/id1491913803