WWDC Quick Look 💓 By SwiftGGTeam
Handle trackpad and mouse input

Handle trackpad and mouse input

元の動画を見る

ハイライト

UIKit は、UIHoverGestureRecognizer、prefersPointerLocked、allowedScrollTypesMask、buttonMask、modifierFlags、および UIApplicationSupportsIndirectInputEvents を使用して、iPad および Mac Catalyst アプリでトラックパッド、マウス ホイール、追加キー、キーボード修飾子入力を正確に処理できます。


主要内容

iPadOS 13.4 では、マウスとトラックパッドが通常の iPad アプリの日常操作に組み込まれます。ユーザーがポインタをビデオ プレーヤーの端に移動すると、コントロール バーが自動的に表示されることを期待します。 2 本の指で引き出しをスライドさせると、中身も一緒に移動することを期待します。右クリックすると、より直接的なコンテキスト操作が開くことを期待します。これらの動作は、タッチ パスだけでは保証できません。 UIKit は、間接入力用に個別のイベントとジェスチャの入り口を提供します。

このセッションの主な目的は明確です。まず各アプリで一般的な入力を処理し、次にヘビー ユーザー向けの高度なコントロールを追加します。一般的な入力には、ポインターの移動、ポインターのロック、スクロール入力、ピンチ、回転などがあります。高度なコントロールには、マウス ボタン、キーボード修飾子、ジェスチャ受信フィルタリング、互換モードから新しい間接入力イベント モデルへの切り替えなどがあります。

主な違いはイベント ソースです。フィンガータッチは今でも伝統的ですUITouchパス;トラックパッドのスクロールドライブEventType.scroll;正確なつまみと回転が可能になります。EventType.transform; マウス ボタンとキーボードの修飾キーがハングアップします。UIEventそしてUIGestureRecognizer優れた。すべてのインタラクションが押し戻されることを避けるために、アプリはこの情報を正しいレベルで読み取る必要があります。touchesBegan道。


詳細

1. UIHoverGestureRecognizer を使用してポインターの動きに応答する

01:49UIHoverGestureRecognizerCatalina for Mac Catalyst で導入され、iPadOS で利用可能です。これは、ビデオ再生コントロール バーの表示または非表示など、ビューの境界に出入りするポインタに応答するのに適しています。ポインターの外観またはホバー効果を変更する場合は、次を使用する必要があります。UIPointerInteraction

let controlsHover = UIHoverGestureRecognizer(target: self, action: #selector(handleHover))

@objc func handleHover(_ recognizer: UIHoverGestureRecognizer) {
    switch recognizer.state {
    case .began:
        // Pointer entered our view - show controls
        self.showsPlaybackControls = true
    case .ended:
        // Pointer exited our view - hide controls
        self.showsPlaybackControls = false
    default:
        break
    }
}

キーポイント:

  • UIHoverGestureRecognizer他のジェスチャ認識機能と同様に、ターゲット アクションを介して機能します。
  • .began対応するポインタが現在のジェスチャ ビューの境界に入ります。
  • .ended対応するポインタは、現在のジェスチャ ビューの境界を離れます。
  • トランスクリプトでは、ジェスチャ状態とウィンドウ レベルのポインタ フェーズが明確に区別されます。RegionEnteredRegionMovedRegionExited

2.prefersPointerLocked を使用してポインター ロックを適用します

(05:33) iPadOS 14 と Mac Catalyst Big Sur には、ポインターをロックするための API が追加されています。典型的なシナリオはゲームです。ユーザーは画面上でポインターを動かすことを望んでいませんが、相対的な変位によって視点やキャラクターを直接操作することを望んでいます。アプリはビュー コントローラーを通じて設定を表現し、ロックするかどうかはシーンの状態に基づいてシステムによって最終的に決定されます。

class GameViewController: UIViewController {

    var shouldLockPointer: Bool = true

    override var prefersPointerLocked: Bool {
        return self.shouldLockPointer
    }

    func disablePointerLock() {
        self.shouldLockPointer = false
        self.setNeedsUpdateOfPrefersPointerLocked()
    }
}

キーポイント:

  • prefersPointerLockedデフォルト値は次のとおりですfalse
  • ロックされたポインターを必要とするビュー コントローラーは、このプロパティをオーバーライドし、現在の設定を返します。
  • ステータス変更後に呼び出されますsetNeedsUpdateOfPrefersPointerLocked()システムに設定を再読み込みさせます。
  • iPadOS 上のシーンは全画面表示であり、foregroundActive;Mac 上のアプリケーション Catalyst は最前面のアプリケーションである必要があります。

(05:53) ビジネス ロジックが実際のロック状態に依存している場合は、シーンのpointerLockState、観察してくださいUIPointerLockState.didChangeNotification。セッションでは、アプリは好みが満たされるとは想定できないことを強調しています。

if let pointerLockState = self.window.windowScene?.pointerLockState {
    self.observer = notificationCenter.addObserver(forName: UIPointerLockState.didChangeNotification,
                                                   object: pointerLockState,
                                                   queue: OperationQueue.main) { (note) in
        guard let lockState = note.object as? UIPointerLockState else { return }
        gameEngine.performExpensiveOperationWhile(lockState.isLocked)
    }
}

キーポイント:

  • pointerLockState多分nil、現在のシーンがポインター ロックをサポートしていないことを示します。
  • isLockedシステム分析後の実際の状態です。
  • スライド オーバー、コントロール センター、通知センター、またはフォアグラウンド アプリの状態変化がロック状態に影響を与えると、通知がトリガーされます。

3. UIPanGestureRecognizer のスクロール入力を開く

09:54UIScrollViewパン ジェスチャ認識機能はすべてのスクロール入力を処理しますが、通常のUIPanGestureRecognizerデフォルトではスクロールマスクはありません。カスタムドロワー、プルして更新、キャンバスのドラッグなどのインタラクションをシーンに応じて設定する必要があります。allowedScrollTypesMask

// Enable scroll input for touch surface devices

self.drawerPan.allowedScrollTypesMask = [.continuous]


// Enable scroll input for scroll wheel devices as well

self.pullToRefreshPan.allowedScrollTypesMask = [.all]

キーポイント:

  • .continuousトラックパッドなどのタッチ面での連続スクロールに使用されます。
  • .all連続スクロールとマウス ホイール入力の両方を受け取ります。
  • セッション内のドロワーの例では、スクロール ホイールでサイド コンテンツを開くのは不自然であると設計者が判断したため、連続スクロールのみを受け入れます。
  • カスタムのプルツーリフレッシュを選択可能.all, これにより、トラックパッドとスクロール ホイールの両方をトリガーできるようになります。

4. ピンチと回転に正確な変換イベントを使用させます

10:41UIPinchGestureRecognizerそしてUIRotationGestureRecognizerトラックパッドでのピンチと回転は自動的に処理されます。古いプロジェクトはデフォルトで互換モードになっており、UIKit は 2 本指のタッチをシミュレートします。参加するUIApplicationSupportsIndirectInputEventsその後、アプリはEventType.transform、ジェスチャの精度が高くなります。

<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>

キーポイント:

  • この Info.plist キーは、ポインター操作、クリック、スクロール入力、またはトラックパッド ジェスチャーを有効にするための前提条件ではありません。
  • 有効になりますTouchType.indirectPointerそしてEventType.transform
  • 変換イベントによって駆動され、numberOfTouches戻ります0locationOfTouch:inView例外をスローする可能性があります。
  • 対処するUIPanGestureRecognizerUIPinchGestureRecognizerUIRotationGestureRecognizer依存イベントの判定を新しいイベントに移行する必要がある場合shouldReceive方法。

5. buttonMaskRequired を使用して、指定されたマウス ボタンを受け取ります

14:48UIEvent.ButtonMaskクリックされたときに押されたポインティング デバイスのボタンを表します。UITapGestureRecognizer追加したbuttonMaskRequired、ジェスチャをトリガーするために特定のボタンを直接要求できます。

self.thirdMouseButtonTap.buttonMaskRequired = .button(3)

キーポイント:

  • .button(3)マウスの 3 番目のボタンに対応するマスクを返します。
  • この記述方法は、エディターの一時的なツールに 3 番目のキーをバインドするなど、プロのユーザーが追加の操作を追加するのに適しています。
  • buttonMask同時に存在するUIEventそしてUIGestureRecognizer, ただし、読むタイミングが異なります。

(15:07) キーボード修飾キーが渡されましたUIKeyModifierFlagsとして表現され、暴露されるmodifierFlags。ジェスチャーコールバックでは、修飾キーに応じて応答モードを変更できます。

func handleHover(_ recognizer: UIHoverGestureRecognizer) {

    // Show chapter controls if alt is pressed
    let showChapterControls = recognizer.modifierFlags.contains(.alternate)

    // ...
}

キーポイント:

  • .alternateOption/Alt 修飾キーに対応します。
  • 例の同じホバー ジェスチャでは、通常のホバーでは再生コントロールを表示し、オプション ホールドではチャプター コントロールを表示できます。
  • このタイプの高度なインタラクションは、頻度は低いが非常に効率的な隠れた機能に適しています。

6. shouldReceive(_event:) でイベントをフィルターする

(16:38) 新規UIGestureRecognizerそしてUIGestureRecognizerDelegate処理前にジェスチャを承認または拒否できるメソッドUIEvent。セッションに関する特別な注意事項: これらのメソッドは、ジェスチャがイベントを完全に処理する前に発生するため、以下の内容を読む必要があります。UIEventの属性UIGestureRecognizerヒステリシス特性オン。

class SecondaryClickGesture: UIGestureRecognizer {

    override func shouldReceive(_ event: UIEvent) -> Bool {
        // Must look at the event’s mask, not the gesture’s
        return event.buttonMask == .secondary
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
        // Touch handling code ...
    }
}

キーポイント:

  • shouldReceive(_ event:)カスタム ジェスチャが二次クリックのみを受信するようにすることができます。
  • このステージevent.buttonMaskは最新の値です。
  • gestureRecognizer(shouldReceive touch:)スクロールや変換などの非タッチ イベントはオーバーライドされません。

(17:36) Control + 主キーのクリックを副クリックと同等にしたい場合は、両方をチェックします。buttonMaskそしてmodifierFlags

class SecondaryClickGesture: UIGestureRecognizer {

    override func shouldReceive(_ event: UIEvent) -> Bool {
        // Must look at the event’s properties, not the gesture’s
        let secondaryClick = event.buttonMask == .secondary

        let controlClick = event.buttonMask == .primary && event.modifierFlags == .control

        return secondaryClick || controlClick
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
        // Touch handling code ...
    }
}

キーポイント:

  • secondaryClick右クリックまたは 2 本指のクリックをオーバーライドします。
  • controlClickメインボタンと Control 修飾キーをオーバーライドします。
  • このモードは、コンテキスト メニュー、インスペクター、プロ仕様の編集ツールなどの操作に適しています。

(18:10) デリゲート メソッドはホバー イベントをフィルターすることもできます。以下の例では、Option/Alt を押している間、クローズド キャプションのホバーのみを受け取ります。

let ccHover = UIHoverGestureRecognizer(target: self,
                                       action: #selector(handleClosedCaptionHover))

ccHover.delegate = self

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
                       shouldReceive event: UIEvent) -> Bool {

    if gestureRecognizer == self.closedCaptionHover {
        return event.modifierFlags.contains(.alternate)
    }

    return true
}

キーポイント:

  • ジェスチャ デリゲートは、さまざまなジェスチャに対してさまざまなフィルタ条件を定義できます。
  • event.modifierFlags.contains(.alternate)クローズド キャプション ホバーを受け取るための唯一の条件です。
  • 他のジェスチャが返されるtrue、元のイベント フローを維持します。

重要ポイント

1. ビデオプレーヤーのポインターフローティングレイヤー

機能: ポインタが再生領域に入るとコントロール バーが表示され、Option/Alt を押しているとチャプタまたは字幕のエントリが表示されます。

実行する価値がある理由: セッション ホバーの例と修飾子フラグの例は、これら 2 つのインタラクション レベルを正確にカバーしています。

開始方法: プレーヤービューを追加しますUIHoverGestureRecognizer、コールバックで読み取るrecognizer.stateそしてrecognizer.modifierFlags

2. ゲームまたは 3D ビューのポインター ロック モード

対処方法: 全画面ゲーム、3D モデリング、またはマップ ローミング シナリオでの視点制御には、相対マウス移動を使用します。

実行する価値がある理由:prefersPointerLockedそしてUIPointerLockState.isLockedアプリがロック設定を表現し、システム解析状態に基づいてレンダリングまたは入力ロジックを調整できるようにします。

開始方法: メインビューコントローラーでオーバーライドしますprefersPointerLocked、観察するUIPointerLockState.didChangeNotification、存在するisLockedtrue の場合、相対変位制御が有効になります。

3. ドロワーとキャンバスのタッチパッドスクロールをカスタマイズする

何をすべきか: サイドドロワー、タイムライン、キャンバスパン、プルトゥリフレッシュサポートトラックパッドの2本指スライドとマウスホイールを作成します。

実行する価値がある理由: 平均的UIPanGestureRecognizerデフォルトではスクロール入力は受け付けられません。ユーザーは、タッチパッドでリストをスクロールできるが、カスタム領域をドラッグできないという問題に遭遇します。

開始方法: それぞれのカスタム パン ジェスチャ設定allowedScrollTypesMask、タッチパッドの連続スクロールが必要な場合に使用されます。.continuous、ローラーサポートが必要な場合に使用されます.all

4. プロフェッショナルツールのマルチボタン素早い操作

対処方法: 描画、編集、IDE、スプレッドシート アプリで、マウスの 3 番目のボタン、右ボタン、および Control + クリックを別のツールまたはコンテキスト操作にマップします。

実行する価値がある理由:buttonMaskRequiredevent.buttonMaskそしてevent.modifierFlags正確な入力を対応するジェスチャーに引き継ぎ、モードの切り替えを減らすことができます。

開始方法: 簡単なタップ使用UITapGestureRecognizer.buttonMaskRequired;複雑なフィルタリングが書かれていますshouldReceive(_ event:)または代表者のgestureRecognizer(_:shouldReceive:)真ん中。

5. タッチ互換モードから間接入力に移行する

対処法: トラックパッドのピンチと回転をすぐに実行します。EventType.transform、使用しますTouchType.indirectPointerマウスのクリックと指のクリックを区別します。

実行する価値がある理由: 古い互換モードはタッチをシミュレートするため、正確なジェスチャが誤って他の認識機能に接触する可能性があります。新しい間接入力パスにより、ジェスチャのセマンティクスがより明確になります。

開始方法: Info.plist に追加しますUIApplicationSupportsIndirectInputEvents、依存関係を確認してくださいnumberOfToucheslocationOfTouch:inViewそしてgestureRecognizer(shouldReceive touch:)コード。


関連セッション

コメント

GitHub Issues · utterances