ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
トラックパッドとマウスの入力のハンドリング
トラックパッドやマウスからのインダイレクトな入力について、iPadやMac Catalyst Appを最適化することで、ユーザーにより汎用性のある体験を提供できます。これらのデバイスから送られる新しいイベントに対して、Appが素早く反応できるようにする方法を学びましょう。ポインタの動きの操作、ポインタ固定の有効化、スクロール入力とトラックパッドジェスチャーのハンドリング、さらに、ジェスチャーリコグナイザのイベント受け入れ可否について学習します。プロユーザーを満足させ、そして、Appにより上質な操作感覚をもたらすために、キーボードモディファイアやポインティングデバイスボタンによるジェスチャー動作を変更する方法など、先進的な機能をどのように実装するかについてもご説明します。 ポインタによる操作の詳細な内容を学習し、セッションの内容を深く理解するには、 "Build for the iPadOS Pointer"、"Bring Keyboard and Mouse Gaming to iPad"、"Support Hardware Keyboards in Your App"をご覧ください。
リソース
関連ビデオ
WWDC22
WWDC20
WWDC19
-
ダウンロード
こんにちは WWDCへようこそ “トラックパッドとマウスの入力の操作” スティーブ・モウズリー UIKit エンジニアです “トラックパッドとマウスの入力の操作” セッションです このビデオのトークは アプリケーションが間接入力メカニズムに 反応するようにする方法です 例えばトラックパッドやマウスなどに macOS Catalinaと iPadOS 13.4で使われています 全てのアプリケーションに適用する 一般的なアップデートがあります そしてその上を行く高度な アップデートも存在します
通常のアップデートで扱うのは ポインタの動き ポインタのロック スクロール入力 そしてトラックパッドのジェスチャー
高度なアップデートでは Button maskおよび キーボードモディファイア イベントの受け入れ または拒絶に使うのは 新しい UIGestureRecognizer と UIGestureRecognizerDelegateメソッド 間接入力デバイスとタッチを区別し Info.plistで新しい動きを選択します
まずは通常のアップデートを見てみましょう
マウスまたはトラックパッドで アプリと対話することが普通です 画面は触りません ポインタがこのregionで動いている時の Safariのツールバーに注目してください またはポインタがタブに向けられたときに タブを閉じるボタンがどう見えるか SafariはUIHoverGestureRecognizerで このようなポインタの動きに応答します
UIHoverGestureRecognizer Catalinaの Mac Catalystで紹介されました いまではiPadOSで利用可能です これは普通のジェスチャー認識器で Macでの操作と同じように iPadで使用します UIApplication.sendEventを 使わない場合 新しいイベントタイプ EventType.hover に 動かされていることに気づきます
UIHoverGestureRecognizerを ターゲットとアクションでインスタンス化します その他のジェスチャー認識器で するのと同様にです アクションコールバックでは ジェスチャー状態から切り替えて 適切なアクションを取ります
ジェスチャー状態「began」は ジェスチャービュー境界に入る ポインタにマップされ
「ended」はジェスチャービュー境界から出る ポインタにマップされることに これで分かるのは ビデオ再生のコントロールが 基づいているのはポインタが ビュー内にあるかどうかです
iPadまたはMac Catalysit アプリケーションの タッチを観察すると ポインタの動きを監視する新しいフェーズが いくつかあるのが分かります
これらのフェーズはウインドウ内の ポインタ全般の動きをマップします RegionEnteredはポインタがウインドウに 入ったことを意味します RegionMovedはポインタがウインドウ外に ある事を意味しますが またクリックされたり押されたりはしていません RegionExitedはウインドウから 出たことを意味します
注意していただきたいのは これらのフェーズが必ずしも UIHoverGestureRecognizer で説明した ジェスチャー状態と一致しないことです UIHoverGestureRecognizer状態が マップするのは ジェスチャービューの境界内で ポインタが何をしているかだけです 一方フェーズはポインタがウインドウ内で 何をするかに関係します UIHoverGestureRecognizerをポインタの 動きに応答するために使用するか 先ほどのSafariの例でみたようにコンテンツを 非表示または公開するのに使います
ポインタの外観を変更するためには 使用しないでください ホバー効果を適用するためにもです なぜならその用途には UIPointerInteractionがあるからです
UIHoverGestureRecognizer に関しては 2019年のトーク 「Taking iPad Apps for the Mac to the Next Level」 をご視聴下さい もしポインタの外観変更に興味が あるようでしたら 「Build for the iPadOS Pointer」をみて下さい
ポインタの動きへの応答に加えて ゲームのような一部のアプリケーションは ポインタの動きをロックしたいものです iPadOS 14 と Mac Catalyst Big Sur新製品として 紹介したAPIではそれが可能です ご使用は簡単です
UIViewController APIの ロックプリファレンスを設定し 新しいUIPointerLockStateで 解決値を確認します それだけです ポインタは共有のリソースです ですので究極的にはポインタがロック されるべきかどうかはシステムが決定します つまりお好みのポインタロック状態が 適用される場合とされない場合があります それではこの2つのAPIが どう機能するか見てみましょう
View ControllerはprefersPointerLockedの値を TRUEと設定します 要求事項があっている限り システムはあなたのシーンのロック値を 同様にTRUEと設定します その状態はあなたのUIPointerLockStateに 反映されます
コンテンツを表示する必要がある時に ロック解除ができなかったらどうなるか? ゲーム中にネットワークエラーが あったとしましょう そしてUIAlertControllerを 表示したいとします ユーザはポインタを使用して 接触することが予想されます その他のシステムの場合と同じように
問題ありません prefersPointerLockedの デフォルト値はFALSEです UIAlertControllerが prefersPointerLocked値は システムが監視しているので ポインタのロックが解除されます
View Controllerが存在していても していなくても あなたのシーンのポインタロック値は 自動的にアップデートされます そのためこの状態を監視し続けなくても いいのです
あるシーンでポインタを ロックしたい場合は prefersPointerLockedプロパティを View ControllerでTRUEに
この値を無効または変更したい場合は setNeedsUpdateOfPrefersPointerLocked を 呼び出してください
あなたのアプリケーション内の 現在のロック状態を見る必要がある場合 ポインタロック状態を シーンから入手してください そしてisLockedプロパティを見て下さい ここにあるオブジェクトに シーンのポインタロック状態の 変更を通知します
pointerLockStateオブジェクトを 入手したら観察するために登録します
UIPointerLockState.didChange Notificationがポストされると クロージャが実行されます そしてUIPointerLockStateの isLocked値は アプリケーションの他の部分に 引き渡されます
ダイアグラムで説明したように あなたのシーンが満たさなければならない 要求事項がいくつかあります お好みのポインタロック値を考慮するためです 要求事項はプラットフォームごとに 異なります まずは iPadOSから見てみましょう
まず あなたのシーンはフルスクリーンである 必要があります つまりSplit View multitasking または Slide Overでは使えません また Slide Overに 他のアプリケーションは存在できません フルスクリーンとは UIRequiresFullScreen Info.plistキーが 使えない訳ではなく 単にシーンが画面全体を 占めていなければならないという意味です
次にあなたのシーンはforegroundActive 有効状態である必要があります これは何の理由であれ無効であっては ならないという事です 例えばControl Centerや Notification Centerが 表示されている等の理由です
Mac Catalystではあなたのアプリケーションは 最前面でなければなりません システムがprefersPointerLocked値を 考慮するために必要です
複数のウインドウがある場合 ポインタをロックしたいウインドウが 前面に配置されている必要があります
アプリケーションがこれらの要求を 満たさないと ポインタのロックが無効になります iPadOSでは Slide over アプリケーションが 表示されている時 あなたのアプリケーションが最前面でない場合は macOSが表示されている時 isLocked がFALSEに変更し通知されます UIPointerLockStateDidChange Notificationを通して
しかしポインタを再びロックするために 特に何もする必要はありません システムは継続的にこれらの要求事項を 評価しているからです ですから状況が変化すると ポインタロック状態も変化します あなたのシーンで setNeedsUpdateOfPrefersPointerLockedを 呼び出す必要はありません
ポインタをロックするか決めるのは システムの裁量でしたね これらの要求事項は変更される可能性があり ユーザの行動によって報告されています ですからあなたのアプリケーションは prefersPointerLocked値が必ず適用されている と推定するべきではありません isLockedに対する変更をいつでも観察し アプリケーション内で 正しく対応する必要があります
最後にポインタロックは 全シーン利用可能ではないため UISceneのpointerLockStateプロパティは nilを返します ロックが利用可能ではないことを 示唆するためです ポインタをロックするときは トラックパッドとマウスから関係する動きも 見る必要があります この情報については 「Bring Keyboard and Mouse Gaming to iPad」 ビデオをご視聴ください
それではスクロール入力処理について お話ししましょう
確認しなければならない重要なことは アプリケーションの全regionが 接続されたポインティングデバイスに 正しく応答しているかどうかです ユーザが指で何かを パンすることができる場合 コンテンツをパンすることを期待します 2本指のジェスチャーかマウス スクロールホイールを使って
ここで見えるのは Control Centerの カスタムコントロールが トラックパッド上で2本指でパンすることが アップデートされていたことです
ご自分のアプリケーションで スクロール入力に対応します それはUIPanGestureRecognizerの allowedScrollTypesMaskプロパティを 更新することで行います 対応したいスクロールタイプ 1セットを与えるだけで ジェスチャーのEventType.scroll スクロールが有効になります
UIScrollViewのパンジェスチャー認識器は 全種類のスクロール入力に対応するために allowedScrollTypesMaskを更新します しかし標準UIPanGestureRecognizerには デフォルトによるマスクはありません アプリケーションのパンジェスチャーのために このプロパティを更新する必要があります
メインビューの両側にあるコンテンツを 隠すアプリケーションを持っているとしましょう ユーザはこのコンテンツを水平スワイプで 表すことができます これはパンジェスチャーによって行われます あなたのデザイナがもし このコンテンツをスクロールホイールで 露呈することが自然でないと感じた場合 このジェスチャーでは 連続スクロールタイプのみをサポートします
パンジェスチャーのallowedScroll TypesMaskを更新するだけです UIScrollTypeMask.Continuousに 対してです おそらくアプリケーションにカスタム pull to refresh操作があります これもパンジェスチャーによって行われています このジェスチャーでは 全種類のスクロール入力に 対応する必要があるかもしれません allowedScrollTypesMaskを UIScrollTypeMask.allへと 更新するだけの話です
トラックパッドのピンチジェスチャーや 回転ジェスチャーは更に簡単です 単にUIPinchGestureRecognizerと UIRotationGestureRecognizerを使用します 全てのアプリケーションがこれらの 間接ジェスチャータイプに対応できるように これらの認識器は互換モードを使用します デフォルトでgesture simulating タッチによって動きます UIKitはこれらのタッチを固定した 一定の距離だけ離して作ります そしてタッチ画面の動きに応じて その動きをシュミレートします
iPadOS 13.4と macOS Catalina 10.15.4 から始まり アプリケーションはこれらのジェスチャーを 互換モードから外せます そして新しいイベントタイプ EventType.transformに対応します
このイベントタイプは入力デバイスから 直接来ています そのため正確なピンチおよび 回転ジェスチャーを可能にします ユーザが望んでいるまさにその通りに
この新しいイベントタイプを入手し これらの動きを互換モードから外すには アプリケーションのInfo.plistに キーを追加する必要があります これについては後で詳しく説明します
嬉しいことに これらのシナリオには 追加コードは必要ないという事です キー付きでもキー無しでも UIPinchGestureRecognizerと UIRotationGestureRecognizerは この入力にどう対応するかが分かっています そしてInfo.plistキーをアプリケーションに 実際に追加する際には 新しいイベントタイプを処理するために コードを書く必要はありません 勝手に機能してくれます
注意したいのは Info.plistを使用する場合 これらのジェスチャーはトラックパッド 入力中はタッチで動かないという事です その場合 numberOfTouchesがゼロに戻ります そしてlocation(ofTouch:in:)ビュー が例外を投げかけます
これは全てのアプリケーションに 適応する一般のアップデートです
今度は更に高度なアップデートについて お話しましょう プロユーザの皆さんを驚かせ喜ばすために
Button Maskとkey modifierは すばらしい方法です 高度な機能をアプリケーションに追加します
コンテキストメニューは Button Maskを使用します 2本指のタップと第2次クリックを認めるために そのためより効率的なUIを提供します
そしてNumbersは key modifierを使用します そのため複数列をポインタと Shiftkey modifierで選択できます 自分の指でするのと全く同じようにです
UIEvent.buttonMaskは新しいタイプの iOSで ポインティングデバイスでクリックしながら 一連のボタンを押します
UIEvent およびUIGestureRecognizer の両方で buttonMaskとして表示され デバイスの主ボタンだけに 応答する便利な方法を提供し 2本指のタップと2番目のマウスボタンに 応答する機能を作成するかー 多数のマウスボタンをターゲットにします UIGestureRecognizer上のbuttonMaskが 最後に処理されたイベントからです
使用前に特定のbuttonMaskを要求する 簡単な方法が必要な場合に備えて UITapGestureRecognizerと buttonMaskRequiredを更新しました buttonMaskを提供するだけで終了です buttonMaskには便利な機能まであります 多数のボタンに対して適切な マスク値をリターンします buttonMaskRequireと合わせれば 多数のマウスボタンが本当に 簡単にターゲットになります アプリケーションの 高度機能用アクセラレータです
UICommandまたは UIPointerInteractionを ご利用の場合 UIKeyModifierFlagsはよくご存じでしょう これはイベント中に押される一連の キーボードモディファイアです 私たちはUIKeyModifierFlagsをUIEventと UIGestureRecognizerの両方に装備しました modifierFlagとして このプロパティはジェスチャー コールバック中に使用でき イベントにどう応答するかを変更します 例えばSafariでCommandキーを押しながら リンクをクリックすると 新しいタブでリンクが開きます buttonMaskと同じように UIGestureRecognizerのmodifierFlagsは 最後に処理された イベントから表示されます
アプリケーションでの 素晴らしいキーボード体験については "Support Hardware Keyboards in Your App" をご視聴ください
buttonMaskとkey modifierは 簡単に使えます 3番目のマウスボタンをターゲットにするのも buttonMask.buttonを使用して UITapGestureRecognizer. buttonMaskRequiredに適切なマスクを入手する たったそれだけ
UIHoverGestureRecognizer exampleに 戻りましょう modifierFlagの使い方を見ます 以前 ビデオ再生コントロールが ポインタがジェスチャーのビューに 入るたびに公開していました オプションとしてchapter selection コントロールが UIKeyModifierAlternateが 押されるたびに見たければ modifierFlagがその値を含むか どうかを確認するだけです buttonMaskとmodifierFlagは UIGestureRecognizerDelegate用の 新しいAPIとUIGestureRecognizer サブクラスと合体すると特にパワフルです このメソッドはジェスチャーがイベントを 処理している時に必要です そのためUIPinchGestureRecognizerは EventType.scrollについて質問されません このメソッドはイベントを受けるか 拒否するかの選択の機会を提供します buttonMask modifierFlag または その他のプロパティに基づいて決定します これらのメソッドは イベントがジェスチャーによって完全に 処理されるために起こるため UIGestureRecognizerのbuttonMask および modifierFlagsは イベントで見つかった 新しい値を含みません これらのメソッドでこのようなプロパティの どちらかを点検している場合は UIEventで値を見て下さい UIGestureRecognizerではありません UIPanGestureRecognizerやUIPinchGesture Recognizerのようなジェスチャーは 接触無しベースの複数の イベントに対応するため イベント関連コードは動かす必要があります gestureRecognizer(shouldReceive touch:) のようなイベント関連コードは 2つの新しいメソッドのいずれかに
これをアプリケーション でどの様に使えるか 例を見てみましょう UIGestureRecognizer サブクラスがありますが 第2次buttonMaskとの イベントのみを受け付けます これは2本指のタップによってのみ 駆動される機能で行います または第2次マウスボタンクリックでも行います
まずはジェスチャーサブクラスメソッド shouldReceiveEventをオーバーライドします その方法ではイベントにある buttonMaskが 第2時と確実に同じであるかどうかを 確認するだけです もしそうであればイベントを受け付けます 違う場合は拒否します 先ほど言いましたように UIGestureRecognizerにある一方で そのプロパティはこのメソッドでは 考慮するべきではありません shouldReceive Eventは イベントの前に起こり ジェスチャーによって そのためUIGestureRecognizerの buttonMaskはこの時点では最新ではありません
クリックとコントロールkey modifierが 同じアクションを第2次クリックとして 行うのは一般的です 私たちの例でも同じように アップデートできます shouldReceive methodを 更新しなければならないだけです
まず、buttonMaskが確実に 第1次であることを確認します
modifierFlagsがUIKeyModifier Controlと 同等であるかを確認します
第2クリックまたはコントロールクリックならば イベントを受け付けます そうでなければ拒否します
ビデオの例にもう一度戻りましょう クローズドキャプションコントロールを 示すビデオに もう一つのホバージェスチャーを ユーザは設定メニューですでに この機能を持っています ただし私たちはそれをより 早く行う方法をkey modifierで提供します
以前のようにHoverGestureRecognizerを インスタンス化します
今回は私たち自身をデリゲートとします そしてgestureRecognizershouldReceive イベントメソッドを設定します
このメソッドでは UIKeyModifier Alternate が 押されていたらイベントを受け付けます そうでなければ拒否します
アプリのポインタサポートを 改善する方法を考慮しているとき ポインティングデバイスから発生したタッチと 指から発生したタッチを 区別したい場合があります これは特に アプリケーションにカスタムhit testingコードが たくさんある場合に有効です
ポインタの方が指よりも正確であるため これらのタッチの拡張hit testing regionを 縮小でき より正確な体験を提供できます ポインティングデバイスからのタッチには 新しい indirectPointerのTouchTypeが 与えられます UIApplicationSupportsIndirectInput EventsInfo.plist keyを選択していることが条件です
このタッチタイプを現存のAPIと ご利用できます 例えば UIGestureRecognizer. allowedTouchTypesのようなAPIです そうするとポインタクリックにのみ応答する ジェスチャーや 指ベースタッチにのみ応答する ジェスチャーがご利用できます
UIApplicationSupportsIndirectInput Eventsについてもう少しお話しましょう これはアプリケーションの Info.plistに追加する ブールキーです このキーはポインタ操作を有効にするために 必要ではありません ボタンクリック スクロール入力 トラックパッドジェスチャーにも必要ありません キー付きであろうとキー無しであろうと同じです これが必要なのは 新しいタッチタイプ間接ポインタ および Event Type.transformの場合です
現存するプロジェクトはこのキー設定を 持っていないため追加する必要があります iOS 14および macOS Big Sur SDKから始まり 新しい UIKitおよび SwiftUIプロジェクトは この値がTRUEにセットされています 将来リリースされる製品では デフォルトが変更されます そのためこのキーの値を確かめる必要が なくなります このキーがある場合とない場合に 何が起こるか見てみましょう
まずはUIApplicationSupports IndirectInputEventsを 互換モードから外れると考えると この互換モードを追加したのは ユーザの最初の経験を 素晴らしいものにするためです iPadOS 13.4 に間接入力した時にです ですから他の現存するプロジェクトと同様 このキーが存在していない場合 アプリケーションは 互換モードになっています
ポインティングデバイスからのクリックは TouchType.direct であり 指ベースタッチと同様です ですからこの二つを区別することは できません トラックパッドのピンチと回転は gesture simulating touchesとなり その他のジェスチャーを有効化する 可能性があります
キーが存在しTRUEであれば アプリケーションは 互換モードではありません そして新しい機能が有効化されています ポインティングデバイスからのクリックは TouchType.indirectPointerであり ターゲットと機能の変更が可能です 精密なポインティングデバイス用です トラックパッドのピンチおよび回転は 新しいイベントタイプを作ります 入力デバイスEventType.transformから 直接的にです これで精密ピンチおよび 回転ジェスチャーができます 他の認識器を偶然有効化する事は ありません
このキーを使用すると間接入力の 新しい世界に完全に突入します iPadOS と Mac Catalystの上において 幾つか注意することがあります
EventType.scrollや EventType.transform のような新しいイベントタイプは タッチベースではありません ですからタッチ関係ジェスチャー認識器 APIには注意する必要があります
UIPanGestureRecognizerと UIPinchGestureRecognizer そしてUIRotationGestureRecognizerが これらの新イベントタイプに駆動された場合 numberOfTouches はゼロに戻ります そしてlocation(ofTouch:in:)ビューは 例外になるかもしれません
またお使いのコードで こららのジェスチャー用 shouldReceive touch デリゲートメソッドのものは これらのイベントによって駆動されるときは 実行されません このキーから外れた後は UIPinchGestureRecognizer や UIRotationGestureRecognizerは 互換モードから除外されます そのためそのモードから偶然有効化された ジェスチャーは トリガされることはなくなります この新しい間接入力の世界では ジェスチャーは複数のタイプの イベントタイプに応答します そのためあなたのジェスチャー認識器が 認識しておくと役立つかもしれません そのためには先ほどお話したshouldReceive メソッドをご利用することができます EventType.touches に応答するとき numberOfTouchesやlocation(ofTouch:in:)ビュー のようなAPIをご利用いただけます その他のイベントに応答している場合は これらのメソッドは避けましょう このように簡単にアプリケーションを生かし トラックパッドとマウス入力で アプリケーションを魅力的にすることができます
パンジェスチャー用に コンテンツを隠したり表示したりすることで ポインタ動作に応答します
Info.plist キーをアプリケーションに追加して 新しいTouchType や EventTypeを得ることで ポインタベースタッチ用に機能を カスタマイズすることができます またアプリケーションで精密なピンチおよび 回転ジェスチャーを使用できます
新しいプロパティおよびジェスチャー 認識器APIをご利用ください そしてボタンを押すか キーボードモディファイアの応答方法を変更して ユーザを感激させましょう ご視聴ありがとうございます みなさんのアップデートされた アプリケーションを見るのが楽しみです
-
-
1:49 - UIHoverGestureRecognizer
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 } }
-
5:33 - prefersPointerLocked
class GameViewController: UIViewController { var shouldLockPointer: Bool = true override var prefersPointerLocked: Bool { return self.shouldLockPointer } func disablePointerLock() { self.shouldLockPointer = false self.setNeedsUpdateOfPrefersPointerLocked() } }
-
5:53 - UIPointerLockState.isLocked
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) } }
-
9:54 - 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]
-
14:48 - Requiring a 3rd mouse button click
self.thirdMouseButtonTap.buttonMaskRequired = .button(3)
-
15:07 - Changing response for .alternate keyboard modifier
func handleHover(_ recognizer: UIHoverGestureRecognizer) { // Show chapter controls if alt is pressed let showChapterControls = recognizer.modifierFlags.contains(.alternate) // ... }
-
16:38 - Only handle secondary clicks
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 ... } }
-
17:36 - Only handle secondary clicks or control clicks
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 ... } }
-
18:10 - Only receive hover events with the .alternate modifier pressed
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 }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。