ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
デスクトップクラスの編集操作を取り入れる
デスクトップクラスの高度な編集機能は、Appの生産性を向上させます。Mac Catalyst を使用して、編集機能にすばやくアクセスし、iPadOS アプリケーションを macOS で快適に動作させるために、UI にインラインでより多くのインタラクションを提供する方法について説明します。また、高度にカスタマイズ可能な検索インタラクションを紹介し、システムUIを使用してApp内のコンテンツをまとめて検索する方法についても学びます。
リソース
- Building a desktop-class iPad app
- Supporting desktop-class features in your iPad app
- UIEditMenuInteraction
- UIFindInteraction
関連ビデオ
WWDC22
WWDC19
-
ダウンロード
こんにちは 「デスクトップクラスの 編集操作を取り入れる」へようこそ 私AndyはUIKitフレームワークの エンジニアで 同僚のJamesと参加します iPadは シンプルで使いやすい インタラクションを損なうことなく 常に進化を続けています このセッションでは Appを デスクトップクラスに変える 新しい編集インタラクションについて学びます まずは iOS 16で進化した 新しい編集メニューについて
その後 新しいシステムの 検索と置換をご紹介します iOS 16では 編集メニューのデザインが一新され 親しみやすさはそのままに よりインタラクティブで アクションが発見しやすくなっています 編集メニューは現在使っている インプットメソッドに応じて 代替表示されます タッチ操作では 編集メニューは従来通り コンパクトな外観ですが ページング動作が改善されて アクションがより発見しやすくなっています
Magic Keyboardやトラックパッドでは セカンダリまたは右クリックで コンテキストメニューが表示され よりデスクトップクラスに 近い操作性を実現します iPhoneでタッチ操作すると 新しい編集メニューが出ます
Mac Catalyst Appでは Macユーザーに馴染みのある コンテキストメニューを表示します iOS 16では 新しいデータ検出器の統合により テキスト編集メニューを大幅に改善しました インラインでの単位や通貨の変換ほか 選択テキストによって 文脈に応じたアクションを表示する スマートルックアップを含みます 例えば Safariで住所を選択すると 既存の編集メニューの操作に加えて 「経路を検索」「マップで開く」といった 地図ベースのアクションが表示されます 一番の特徴は わざわざ 導入する必要がないこと! テキストインタラクションビュー PDFKitやWebKit Safariなど すべてのテキスト編集メニューで使えます
テキストビューのメニューに アクションを挿入するには 新しいTextViewDelegateメソッドを実装して 選択範囲のテキストに対して表示される項目を カスタマイズしてシステム提供の アクションと共に表示します カスタマイズが必要なければ nilを返すと 標準のシステムメニューになります UITextFieldDelegateやUITextInputにも 同様のメソッドがあり そちらでもカスタマイズ可能です iOS 16ではUIMenuControllerを使った メニュー項目の挿入は非推奨となったので 代わりに新しいメソッドで テキスト編集メニューに メニュー要素を追加する必要があります なぜならメニューコントローラは 必要なくなるからです! これはカスタムアクションを持つ テキストビューの例です テキスト選択時にメニューが表示された時に カスタムの「Hilight」と 「Insert Photo」を表示するようにしました ハイライトアクションを選ぶと実行されます ハイライトするものがない状態で テキストを選択せずにメニューを表示すると システムが提案するアクションの後に 「Insert Photo」だけが表示されます 新しいAPIを使ったアクションの追加方法を紹介します プレゼンテーション時に動的にメニューに アクションを挿入するには UITextViewDelegateメソッド textView:editMenuForText InRange:suggestedActionsを実装します ここでは選択テキストがあるときだけ 「Hilight」を追加したいので このメソッドで追加します
「Insert Photo」は常に有効なので 配列に追加し 常にメニューにアクションを表示させられます カット コピー ペーストの項目を含む システム提案のアクションに 自分のアクションを追加して メニューを返します これで終わりです! UIEditMenuInteractionは新しい 編集メニューを強化するUIInteraction APIで
これを使うと独自のジェスチャーに基づいて テキストビューの外側に 軽量な編集メニューが表示でき 右クリックでコンテキストメニューを表示する ネイティブサポートが用意されています iOS 16ではUIMenuControllerと関連APIは 新しい編集メニューインタラクションに 置き換えられます
ゼロから編集メニューを提示するには まずインタラクションを作成して ビューに追加してから 次にジェスチャーリコグナイザーで メニューを提示するように設定します ポインタの間接的なクリックではなく 直接タッチしたときのみ メニューを表示するには ジェスチャー認識機能の allowedTouchTypesプロパティを 直接タッチのみに設定してください 次に ジェスチャー認識機能をビューに追加します 最後に ジェスチャーリコグナイザーが起動したら ジェスチャーの位置にメニューを表示できる コンテンツがあるかを判断します 次に ジェスチャーの位置にソースポイントを持つ 編集メニューのコンフィグを作成します ソースポイントはメニューに表示する インタラクションのビューで 実行可能なアクションを決定します
設定後 presentEditMenu WithConfigurationを 呼び出してメニューを表示します
選択した「Jello there!」 ビュー内で右クリックすると Appのコンテンツに対して 実行可能なシステムアクションを コンテキストメニューに表示します さらに 選択したビューをタップすると タッチした場所に編集メニューが表示され コンテキストメニューと 同じアクションを表示します これでもいいですがもっと良くなりますよ タッチした場所にメニューが表示される一方で 選択したビューのコンテンツが ブロックされています さらにシステムのデフォルトアクションでない 「複製」をメニューに挿入したいのです 戻ってこれを変更しましょう 選択されたビューの周囲に メニュー表示するには デリゲートメソッドを実装します editMenuInteraction: targetRectForConfiguration です これはメニューを表示する場所を 決めるCGRectを返し インタラクションのビューの 座標空間内にあるものです 実装していない またはCGRectがnullの場合は メニューはコンフィグの ソースポイントから表示されます この場合 メニューが選択したビューに かぶらないようにそのフレームを返します 「複製」を追加するには editMenuInteraction:menuForConfiguration: suggestedActions: を実装し テキストビューのメニューに アクションを挿入したのと同様に システム提案アクションの後に カスタムアクションを追加します
選択したビューをタップすると 「Jello there!」の周りを囲むように 表示されるようになりました 新しい「複製」もメニュー表示時に含み これらを わずか数行のコードで実現しています 見事ですね!
Mac Catalyst Appでは 編集メニューは ユーザーがインタラクションのビューで 右クリックしたときに Macで期待される おなじみのコンテキストメニューに 橋渡しされます iPadイディオムMac Catalyst Appでは プログラムで用意した編集メニューが コンテキストメニューに橋渡しされます MacのイディオムAppでは プログラムによる編集メニューの表示は サポートされていません シームレスな橋渡しのため UIEditMenuInteractionは UIMenuElementファミリーのAPIが ベースになっています これらは サブメニューや画像のサポートなど さらに柔軟で カスタマイズ性の高い機能を提供します UIMenusが初めての方は 「iOS 13向けにUIをモダナイズする」で メニューとアクションについて 詳しく学びましょう UIMenuElement上に構築することは 編集メニューがUIMenuSystemのような すでにメニューをサポートしている 他のAPIにアクセスできるということです 編集メニューは既存の UIMenuSystem.contextシステムを使用して メニューを構築します メニュービルダ レスポンダチェーントラバーサル コマンドバリデーションについて詳しくは 「iPad Appを次のレベルに」をご覧ください
iOS 16では UIMenuに 新しい機能強化を施しました UIMenuに優先要素サイズプロパティを追加して コンテキストメニューで 異なるレイアウトが選択でき メニューをよりコンパクトな 横並び表示にすることで 一列でより多くのアクションが 並べられるようにもなりました ミドルサイズもアクションを 横並びで表示しますが もう少し細かく表示します テキスト編集メニューで 標準の編集メニュー表示のために使用します 最後に 要素サイズを大きくすることで メニューがデフォルトの全幅で表示できます UIMenuElementに 新しい .keepsMenuPresented属性を追加し アクションの実行後も メニュー表示が維持できるようになりました この属性を使用するとメニューを再表示せずに アクションが複数回実行できます これはまだ 新しい編集メニューの 氷山の一角です カスタマイズして テキスト編集機能を拡張しましょう アクションにはタイトルと画像を付けて さまざまな表示スタイルで メニューが完成するようにしましょう 最も重要なことは 新しいUIEditMenuInteractionで カスタマイズ性を高め プラットフォームや 異なる入力メソッド間の 一貫性を向上させることです 新しい編集メニューの追加は 素晴らしい最初の一歩です デスクトップクラスの 編集体験を完成させるため Jamesと交代して 新しいシステムの検索と置換について 説明します
ああ あった! どうも UIKitエンジニアのJames Magahernです 検索と置換についてお話しします iOS 16の新機能として 新しいUIコンポーネントを導入しました システム全体に標準装備され 多くの内蔵Appに含まれるため ユーザーは頻繁に使用する 編集ショートカットで 指の記憶を鍛えることができます iPad上で動作する新しい検索パネルです ハードウェアキーボードの装着時は ショートカットバーを インラインでフローティング表示し ソフトウェアキーボード使用時は その上部に自動的に移動します iPhoneの場合は画面サイズに合わせて よりコンパクトなレイアウトで表示します 自動解除 最小化 キーボード回避は すべてシステムで対応します MacでAppを実行する場合 AppKit の検索バーと同じように動作し ユーザーが Macで期待する 使い慣れたレイアウトで コンテンツにインラインで 検索パネルを表示します
UITextViewやWKWebView PDFViewsを使って Appに テキストコンテンツを表示している場合 ビルトインの検索インタラクションで 「isFindInteractionEnabled」をtrueにすると 始められます簡単でしょう! QuickLookでテキストコンテンツを 表示している場合は 何もせずとも既に利用可能です
ハードウェアキーボードでは command+Fで検索 command+Gで次を検索 command+shift+Gで前を検索など 標準的なシステムショートカットは すべて期待通り機能します Macで実行する場合は メニューバーからアクセスできます コンテンツを表示するビューを ファーストレスポンダーにするだけです ハードウェアキーボードを 使用しないユーザーには findInteractionプロパティの presentFindNavigatorで プログラムから検索機能が呼び出せます ナビゲーションバーアイテムから 利用できるようにするといいかもしれません
Macで実行する場合他にも注意点があります iOSの場合 検索パネルは ソフトウェアキーボード またはショートカットバーの 一部として表示されます Macでは コンテンツにインラインで表示します スクロールビューに検索インタラクションを インストールする場合 自動的にコンテンツのインセットを調整して トレイトコレクションの変更に対応します それ以外の場合はmacOSのUIで 検索パネルを ホストするためのスペースの確保が必要です
さらに虫眼鏡のアイコンをタップすると 標準的な検索オプションを含むメニューが 表示されます UIFindInteractionのoptionsMenuProviderで メニュー内容がカスタマイズできます これはカスタム実装でより重要になってきます 先ほど紹介したビルトインのビューを 使用している場合はこれだけで大丈夫です Appがテキストコンテンツを 完全にカスタム化されたビューや リストビューなどで表示している場合でも 検索インタラクションをAppに追加できます その方法をご紹介します
検索インタラクションの良いところは 任意のビューにインストールできることです 既存のAppに検索と置換の実装がある場合 UIFindInteractionに橋渡しして システムのUIを利用するのは簡単です カスタムビューに 既存の検索の実装がない場合でも 特にシステムキーボードと連携するために UITextInputプロトコルを既に実装していれば 非常に簡単に始められます UIFindInteractionのカスタムビューでの 動作について説明します インストール後findインタラクションの デリゲートをセットアップします findインタラクションデリゲートは findセッションの開始と終了の通知だけでなく UIFindSessionsを処理する責任を負います UIFindSessionは 現在ハイライトされている結果など セッションのすべての状態を カプセル化する抽象的な基本クラスです 「次の結果に進む」 「この文字列を検索する」など UIが要求するすべてのアクションを サービスとして提供します 自分で管理する場合は findインタラクションデリゲートで 提供するUIFindSessionのサブクラスを 選択することができます
すでにAppに既存の検索と置換の実装があり それをシステムUIに橋渡ししたい場合に 有効なオプションです そうでない場合はシステムに状態を任せて 表示されるドキュメントのコンテンツを カプセル化したクラスに UITextSearchingプロトコルを 採用するのがよいでしょう そのためには UITextSearchingFindSessionを返して それをドキュメントクラスと接続します このオプションはカスタムビューにまだ findの実装がない場合に最適です その方法をコードで説明します
この例には カスタムドキュメントクラスと このドキュメントを表示する カスタムビューがあります このビューにUIFindInteractionが インストールされて UITextSearchingFindSessionが ドキュメントに検索対象として提供されます キーボードショートカットが動作するために ビューコントローラやカスタムビューが ファーストレスポンダになれることを 確認してください
findインタラクションを作り セッションデリゲートを提供して処理します 今は ビューコントローラが セッションデリゲートです 検索セッションについては 検索可能なオブジェクトとして あなたのドキュメントを提供する 新しいUITextSearchingFindSessionを返します ドキュメントクラスが UITextSearchingプロトコルに 準拠していることを確認してください
UITextSearchingプロトコルを 実装したクラスは ドキュメント内のテキストを検索します システムはperformTextSearchを呼び出し アグリゲータオブジェクトで結果を渡します その際 アグリゲーターは UITextRangeと連携します これも抽象クラスでテキストを保存するのに 適したデータをカプセル化 するために使用できます 例えば WebKitでテキストを レンダリングするクライアントは DOMの範囲を表すことができます アグリゲーターはスレッドセーフなので バックグラウンドスレッドで 結果を提供できます findインタラクションはカスタムビューで 結果を表示する方法を知らないので decorate()が呼ばれた際に指定された スタイルで検索結果の装飾が必要でしょう UITextSearchingの 検索セッションとプロトコルは 同じインタラクションを使って 複数のドキュメントにまたがる 検索もサポートします Appがメールの会話ビューのように コンテンツを表示し 各「ドキュメント」がメールメッセージの場合 ルートレベルのコレクションビューに 単一の検索インタラクションを インストールして すべてのドキュメントを同時に検索して 異なるドキュメント内の検索結果を ユーザーは簡単に行き来できます これでiOS 16の新しい 検索インタラクションが使えます 多くのテキストコンテンツを 表示するシステムビューでは isFindInteractionEnabledを 必ず有効にしてください 既存の検索の実装を UIFindInteractionに移行します UITextSearchingを実装し 検索をまだ実装していない場合は UITextSearchingFindSessionを使用します 最後に App内でキーボードショートカットが 競合していないことを確認してください iOS 16用に Appの編集インタラクションを リフレッシュし 真のデスクトップクラスにします 新しいテキスト編集メニューを Appで試して カスタムUIを編集メニューの インタラクションに対応させましょう テキストを検索可能にして 生産性を向上させましょう Appに素晴らしい新機能が 導入されるのが楽しみです ありがとうございました いいね! コメント 購読を よろしくお願いします
-
-
2:42 - Adding items into text edit menus
func textView( _ textView: UITextView, editMenuForTextIn range: NSRange, suggestedActions: [UIMenuElement]) -> UIMenu?
-
4:03 - Adding actions into a text view's menu
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) }
-
5:24 - Presenting an edit menu with a custom gesture
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) } }
-
7:13 - Implementing UIEditMenuInteractionDelegate
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]) }
-
10:34 - Using the "keeps menu presented" attribute
UIAction(title: "Increase", image: UIImage(systemName: "increase.indent"), attributes: .keepsMenuPresented) { ... } UIAction(title: "Decrease", image: UIImage(systemName: "decrease.indent"), attributes: .keepsMenuPresented) { ... }
-
12:46 - Find with system views
open var findInteraction: UIFindInteraction? { get } textView.isFindInteractionEnabled = true
-
17:22 - Installing a UIFindInteraction on a custom view
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) }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。