WWDC Quick Look 💓 By SwiftGGTeam
Adopt desktop-class editing interactions

Adopt desktop-class editing interactions

元の動画を見る

ハイライト

iOS 16 では、iPad でのテキスト編集とインタラクティブなエクスペリエンスのために特別にアップグレードされた「デスクトップクラスの編集インタラクション」の概念が導入されています。核となる変更点は、UIEditMenuInteraction交換されたUIMenuController、新しい API は位置を認識した編集メニューを提供し、視覚効果は macOS の右クリック メニューに近くなります。


主要内容

これまでの iPad アプリの一般的な問題は、テキスト編集とカスタム キャンバス間の相互作用に一貫性がないことでした。システム テキスト ビューには、コピー、貼り付け、検索が含まれます。同じ機能をカスタム ビューに追加する場合、多くの場合、ジェスチャ、メニューの位置、キーボード ショートカット、および Mac Catalyst の右クリック メニューを処理する必要があります。

iOS 16 では、この種の編集操作をシステム インタラクションに戻します。テキスト メニューは、入力方法に基づいて切り替わる新しい外観になっています。タッチするとコンパクトなメニューが表示されますが、トラックパッドまたは Magic Keyboard を右クリックするとコンテキスト メニューがデスクトップに近くなり、Mac Catalyst では Mac ユーザーに馴染みのあるメニューにブリッジされます (00:55)。

このセッションは 2 行に分かれています。 1 つ目は新しい編集メニューです。テキスト プロキシ メソッドを使用し、UIEditMenuInteraction代わりのUIMenuController。 2 番目の方法は、検索して置換することです。UIFindInteractionシステム検索パネルにテキストコンテンツにアクセスして、Command-FCommand-Gこのタイプのショートカット キーは、システムの習慣に従って機能します (11:44)。

詳細

テキスト編集メニューは UIMenuController に依存しなくなりました

に行くUITextView編集メニューにはカスタム アクションが詰め込まれています。一般的な方法は、次のことに焦点を当てることですUIMenuController加工を行います。 iOS 16 では、このパスを非推奨として明示的にマークし、代わりに新しいテキスト プロキシ メソッドを使用することを Apple に要求し、カスタムのままにします。UIMenuElementシステム推奨アクション (02:42) の後ろに統合されました。

func textView(
    _ textView: UITextView,
    editMenuForTextIn range: NSRange,
    suggestedActions: [UIMenuElement]
) -> UIMenu? {
    var additionalActions: [UIMenuElement] = []
    if range.length > 0 {
        let highlightAction = UIAction(title: "Highlight", ...)
        additionalActions.append(highlightAction)
    }
    let insertPhotoAction = UIAction(title: "Insert Photo", ...)
    additionalActions.append(insertPhotoAction)
    return UIMenu(children: suggestedActions + additionalActions)
}

キーポイント:

  • editMenuForTextIn rangeメニューが表示されるときに呼び出され、現在の選択に基づいてアクションを動的に決定するのに適しています。 -suggestedActionsこれらは、システムが準備した切り取り、コピー、貼り付け、およびその他のアクションです。 -range.length > 0させてHighlightテキストが選択されている場合にのみ表示されます。 -Insert Photo選択に依存しないので、毎回メニューに追加されます。
  • 戻るUIMenu(children: suggestedActions + additionalActions)、システム アクションを保持してから、ビジネス アクションを追加します。

アップルも次のように述べていますUITextFieldDelegateそしてUITextInput同様の方法もあります。カスタム要件がない場合は返品nil、システムは標準メニュー (02:55) を表示します。

カスタムビューには UIEditMenuInteraction を使用します

テキスト ビューの外では、問題はさらに明らかです。たとえば、カスタム キャンバスでオブジェクトが選択され、ユーザーがそれをクリックまたは右クリックすると、コピー、貼り付け、削除、およびコピーのメニューが表示されます。 iOS 16の答えは、UIEditMenuInteraction04:54)。

let editMenuInteraction = UIEditMenuInteraction(delegate: self)
view.addInteraction(editMenuInteraction)

let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(didTap(_:)))
tapRecognizer.allowedTouchTypes = [UITouch.TouchType.direct.rawValue as NSNumber]
view.addGestureRecognizer(tapRecognizer)

@objc func didTap(_ recognizer: UITapGestureRecognizer) {
    let location = recognizer.location(in: self.view)
    if self.hasSelectedObjectView(at: location) {
        let configuration = UIEditMenuConfiguration(identifier: nil, sourcePoint: location)
        editMenuInteraction.presentEditMenu(with: configuration)
    }
}

キーポイント:

  • UIEditMenuInteraction(delegate: self)編集メニューを表示する対話型オブジェクトを作成します。 -view.addInteraction(editMenuInteraction)コンテンツをホストするビューにインタラクションをインストールします。 -UITapGestureRecognizerタッチトリガーパスの定義を担当します。 -allowedTouchTypes間接的なポインターのクリックが同じタッチ ロジックのセットに到達しないように、直接タッチに制限します。 -location(in:)ユーザーがメニューをトリガーした場所を取り出します。 -hasSelectedObjectView(at:)これはビジネス上の判断で、操作可能なオブジェクトをクリックした場合にのみメニューが表示されます。 -UIEditMenuConfigurationsourcePointどのレスポンダーアクションを実行できるかの決定に参加します。 -presentEditMenu(with:)メニューを表示します。

このコードは「メニューをポップアップする方法」を解決します。次のステップは、「メニューがコンテンツをブロックしてはいけない」と「ビジネス アクションを追加する方法」を解決することです。これらのポイントはどちらもデリゲートで完了します (07:13)。

func editMenuInteraction(
    _ interaction: UIEditMenuInteraction,
    targetRectFor configuration: UIEditMenuConfiguration
) -> CGRect {
    guard let selectedView = objectView(at: configuration.sourcePoint) else { return .null }
    return selectedView.frame
}

func editMenuInteraction(
    _ interaction: UIEditMenuInteraction,
    menuFor configuration: UIEditMenuConfiguration,
    suggestedActions: [UIMenuElement]
) -> UIMenu? {
    let duplicateAction = UIAction(title: "Duplicate") { ... }
    return UIMenu(children: suggestedActions + [duplicateAction])
}

キーポイント:

  • targetRectFor configurationメニューアンカーエリアに戻ります。
  • オブジェクトが見つからない場合に返されます.null、システムは設定のソースポイントに戻ります。
  • 戻るselectedView.frame最後に、コンテンツが覆われないように、選択したオブジェクトの周囲にメニューが表示されます。 -menuFor configurationこのメニューの内容をカスタマイズするために使用されます。 -suggestedActionsまだシステムとレスポンダー チェーンからのものです。 -Duplicateこれはビジネス アクションであり、システム アクションの後に追加されます。

このセットは Mac Catalyst にも対応します。セッションには、Mac Catalyst アプリケーションでは、右クリックすると Mac ユーザーに馴染みのあるコンテキスト メニューにブリッジされることが明確に記載されています。 iPad イディオムの Catalyst アプリケーションでは、プログラムで表示された編集メニューもブリッジされます (08:25)。

メニューアクションは連続して実行可能

編集メニューは連続操作にも対応しています。 iOS 16が与えるものUIMenuElement追加した.keepsMenuPresented連続クリックが必要なインデントの増加や減少などのアクションに適した属性 (10:34)。

UIAction(title: "Increase",
         image: UIImage(systemName: "increase.indent"),
         attributes: .keepsMenuPresented) { ... }

UIAction(title: "Decrease",
         image: UIImage(systemName: "decrease.indent"),
         attributes: .keepsMenuPresented) { ... }

キーポイント:

  • titleさまざまなレンダリング スタイルに人間が判読できる名前を付けます。 -imageコンパクトスタイルとデスクトップスタイルの両方でメニューをより充実させます。 -attributes: .keepsMenuPresentedアクションの実行後もメニューが継続して表示されることを示します。
  • 処理クロージャー内のビジネス ロジックは繰り返しトリガーでき、ユーザーは毎回メニューを再度開く必要はありません。

これはテキスト エディタで特に便利です。ユーザーがインデント、リスト レベル、または形式を調整する場合、連続クリックは、「1 回クリックしてメニューを非表示にしてから再度開く」よりもデスクトップ操作に近くなります。

システム ビューの 1 行で検索を開始します

2 番目の部分は検索と置換です。 iOS 16 では、デバイスの形式に応じて自動的に適応する新しいシステム検索パネルが提供されます。ハードウェア キーボードがある場合はショートカット バーの近くに表示され、ハードウェア キーボードがない場合はソフトウェア キーボードに固定されます。 iPhone ではコンパクトなレイアウトになり、Mac の AppKit 検索バーと同様にコンテンツに埋め込まれます (12:02)。

コンテンツが提供されている場合UITextViewWKWebViewまたはPDFViewデモンストレーションでは、アクセスは非常に少ないです (12:46)。

open var findInteraction: UIFindInteraction? { get }
textView.isFindInteractionEnabled = true

キーポイント:

  • システムビューが提供されましたfindInteraction財産。 -textView.isFindInteractionEnabled = trueシステム検索対話を有効にします。
  • 有効にすると、Command-FCommand-GCommand-Shift-Gシステムルールに従って動作します。
  • ビューは、ショートカット キーのターゲットを持つファーストレスポンダーである必要があります。

ハードウェア キーボードがない場合は、ナビゲーション バー ボタンにエントリを入力して、検索インタラクションを呼び出すことができます。presentFindNavigator。 Mac では、検索パネル用に埋め込みコンテンツ領域を残す必要もあります。インタラクションがスクロール ビューにインストールされている場合、システムはコンテンツ インセットを自動的に調整します (13:43)。

UIFindInteraction へのカスタム テキスト コンテンツへのアクセス

コンテンツが自分で作成したドキュメントやリスト ドキュメントによって伝えられる場合、または独自の検索実装セットがすでにある場合は、UIFindInteractionどのビューにもインストールできます (15:14)。

Apple は 2 つの方法を提供しています: 検索実装がすでにある場合は、自分で販売するUIFindSessionサブクラス。既存の状態をシステム UI にブリッジします。検索実装がない場合は、ドキュメント オブジェクトに実装させます。UITextSearching、その後、戻りますUITextSearchingFindSession16:10)。

let customDocument = MyDocument(string: "")
lazy var customView = MyTextView(document: customDocument)

lazy var findInteraction = UIFindInteraction(sessionDelegate: self)

override var canBecomeFirstResponder: Bool { true }

override func viewDidLoad() {
    customView.addInteraction(findInteraction)
}

func findInteraction(_ interaction: UIFindInteraction, sessionFor view: UIView) -> UIFindSession? {
    return UITextSearchingFindSession(searchableObject: customDocument)
}

キーポイント:

  • customDocument実際のテキスト内容を保存します。 -customViewこの文書の表示を担当します。 -UIFindInteraction(sessionDelegate: self)現在のオブジェクトに検索セッションの提供を担当させます。 -canBecomeFirstResponder戻るtrue, そのため、キーボード ショートカットがここに該当する可能性があります。 -customView.addInteraction(findInteraction)システム検出 UI をカスタム ビューにインストールします。 -sessionFor view呼び出されると を返しますUIFindSession
  • UITextSearchingFindSession(searchableObject:)検索ステータスはシステム管理に任せ、実際の検索は実装に任せるUITextSearchingドキュメントオブジェクト。

成し遂げるUITextSearchingその後、システムは呼び出しますperformTextSearch、アグリゲーターに渡します。結果をアグリゲータに渡すと、結果は次のようになります。UITextRange急行。アグリゲータはスレッドセーフであるため、結果はバックグラウンド スレッドで提供できます (18:19)。

インターフェイスに複数のドキュメント (メール会話ビューと同様の複数の電子メールコンテンツなど) が同時に表示される場合、UITextSearchingそしてUITextSearchingFindSessionまた、表示されている複数のドキュメント間で同じインタラクションを再利用することもサポートされており、ユーザーは異なるドキュメントの検索結果間を移動することができます (19:19)。


重要ポイント

  • 手順: Markdown エディターで選択したテキストに「ハイライト」と「写真の挿入」を追加します。 実行する価値がある理由:UITextViewDelegateeditMenuForTextInシステムの切り取り、コピー、貼り付けを維持しながら、選択に基づいてアクションを動的に追加できます。 開始方法: 実装textView(_:editMenuForTextIn:suggestedActions:)、選択がある場合に追加されますHighlight、常に追加しますInsert Photo

  • 対処方法: ホワイトボードまたはレイアウト ツールの選択したオブジェクトに右クリック メニューと「複製」を追加します。 実行する価値がある理由:UIEditMenuInteraction同じセットで使用可能UIMenuElementタッチ メニュー、トラックパッドの右クリック、Mac Catalyst コンテキスト メニューも機能します。 開始方法:UIEditMenuInteractionキャンバス ビューに追加され、ジェスチャがオブジェクトに当たった後に呼び出されます。presentEditMenu(with:)、デリゲートでオブジェクト フレームを返し、追加しますDuplicate

  • 対処方法: ラージ テキスト リーダーのシステム検索パネルにアクセスします。 実行する価値がある理由:UITextViewWKWebViewPDFView検索インタラクションを有効にするだけで、システム パネルと標準のキーボード ショートカットが表示されます。 開始方法: テキストビューの設定isFindInteractionEnabled = true、ホスティング ビューがファーストレスポンダーになれることを確認し、ハードウェア キーボードのないシナリオでナビゲーション バーの検索ボタンを提供します。

  • やるべきこと: 自分で描いたドキュメントのページ横断検索を実装します。 実行する価値がある理由:UIFindInteractionシステムテキストコントロールに限らず、UITextSearchingFindSessionシステム検索 UI をカスタム ドキュメント モデルに接続する機能。 開始方法: ドキュメント オブジェクトを実装します。UITextSearching、存在するfindInteraction(_:sessionFor:)戻ってUITextSearchingFindSession(searchableObject:)

  • 対処方法: インデント、レベル調整、形式の切り替えなどのアクション用にメニューを予約します。 実行する価値がある理由:.keepsMenuPresentedユーザーが同じメニュー操作を継続的に実行できるようにして、メニューを繰り返し開く回数を減らします。 開始方法: 作成するUIAction時間設定attributes: .keepsMenuPresented、アクションのタイトルと SF シンボル アイコンを提供します。


関連セッション

コメント

GitHub Issues · utterances