WWDC Quick Look 💓 By SwiftGGTeam
Adopt Quick Note

Adopt Quick Note

元の動画を見る

ハイライト

Apple は、Quick Note がアプリケーションのコンテンツを識別するために NSUserActivity を再利用できるようにします。開発者が安定した識別子を設定し、アクティビティの回復を処理した後、ユーザーは現在のコンテンツをメモにリンクし、メモからアプリケーション内の同じコンテンツに戻ることができます。

主要内容

ユーザーは情報のページを見て、一時的なメモを作成したいと考えています。これまでの一般的なプロセスは、Notes に切り替え、タイトルをコピーし、リンクをコピーしてから、元のアプリケーションに切り替えることでした。場所、記事、ドキュメントなどのコンテンツも、コンテキストを説明するためにユーザーに依存します。

(01:15) クイックメモ このアクションをシステム レベルに置きます。ユーザーは、画面の右下隅からクイック メモ フローティング ウィンドウをスライドさせて、フローティング ウィンドウの上部にあるリンク追加メニューに現在の Web サイトまたはアプリケーションのコンテンツを追加できます。フローティング ウィンドウは、移動、拡大縮小、または画面の端に格納することができます。

(02:36) これらのリンクは、コンテンツ自体との関係を確立します。ユーザーが同じ Web サイトまたはアプリのコンテンツに戻ると、隅にクイック ノートの提案が表示されます。 1 回クリックすると、システムは前のメモを開きます。

(03:26) Apple は、Quick Note 用の新しいリンク プロトコルを設計していません。既存の NSUserActivity (ユーザー アクティビティ) システムを使用します。アプリケーションは現在画面に表示されているコンテンツをアクティビティとして登録し、システムはそれを Handoff、Spotlight、Reminders、Quick Note に送信します。

(04:06) クイックノートには、時間の経過とともにコンテンツを照合できる識別子が必要です。この識別子は一意であり、デバイス間で利用可能であり、長期にわたって安定している必要があります。ユーザーが 6 か月後に同じ記事を開いた場合でも、システムはメモと記事を照合できる必要があります。

詳細

1. NSUserActivity を宣言して作成します

(05:31) クイック ノートにアクセスするには 3 つの手順があります: Info.plist でサポートされているアクティビティ タイプを宣言し、現在の画面コンテンツを説明するアクティビティを作成して登録します。NSUserActivity、外部からの受信アクティビティ再開リクエストを処理します。使用するための最初のステップNSUserActivityTypesキー。アプリケーションがアクティビティを処理できるかどうかをシステムが判断するために使用されます。

import UIKit

struct Document {
    let title: String
    let stableIdentifier: String
}

func attachQuickNoteActivity(to viewController: UIViewController, document: Document) {
    let activity = NSUserActivity(activityType: "com.myapp.MyActivityType")
    activity.title = document.title
    activity.targetContentIdentifier = document.stableIdentifier
    activity.userInfo = ["documentIdentifier": document.stableIdentifier]
    viewController.userActivity = activity
}

キーポイント:

  • import UIKit供給UIViewControllerそしてレスポンダ側でuserActivity財産。
  • Document使用titleそしてstableIdentifier現在の画面の内容を示します。
  • attachQuickNoteActivityアクティビティを現​​在のビュー コントローラーにバインドします。
  • NSUserActivity(activityType:)アプリを使用するNSUserActivityTypesで宣言された型文字列。
  • activity.titleこれは、ユーザーがクイック ノートのリンク追加メニューに表示されるテキストです。文書や項目のタイトルをそのまま使用するのに適しています。
  • activity.targetContentIdentifierQuick Note がコンテンツを照合するために使用する永続的な識別子です。
  • activity.userInfoインターフェイスを復元するときに必要なアプリケーションの状態を保存します。
  • viewController.userActivity = activity現在のアクティビティを手動で維持することなく、UIKit で現在のアクティビティを管理できるようにします。

2. 安定した識別子を選択する

(04:06) クイックノートは、リンク識別子として使用できる 3 つの属性をサポートしています。targetContentIdentifierpersistentIdentifierwebpageURL。どちらの方法でも、Quick Note がリンクを生成できます。どの属性を選択するかは、アプリケーションがサポートしたい他のシステム機能によって異なります。

import Foundation

func configureIdentifiers(for activity: NSUserActivity, photoID: UUID, fallbackURL: URL) {
    let stableValue = "photo-\(photoID.uuidString)"

    activity.targetContentIdentifier = stableValue
    activity.persistentIdentifier = stableValue
    activity.webpageURL = fallbackURL
}

キーポイント:

  • configureIdentifiers同じコンテンツの識別子の構成を一元管理して、異なるシステム機能間の違いを減らします。
  • photoID長期安定性の要件を満たす、アプリケーションによって保存された写真の UUID を示します。
  • stableValueローカル ファイル パス、セッション ID、スケーリングなどの一時情報は使用されません。
  • targetContentIdentifier状態の回復に使用でき、iPad のマルチタスク体験に役立ちます。
  • persistentIdentifierシステムの Spotlight インデックスでアプリのコンテンツを識別するために使用できます。
  • webpageURLフォールバック リンクとして使用できます。デバイスにアプリがインストールされていない場合は、対応する Web サイトを開くことができます。

3. iOS でのクイック ノート リンクの処理

(07:15) ユーザーが [クイック ノート] リンクをクリックすると、システムはアプリケーションを起動し、シーン デリゲートを呼び出します。willContinueUserActivityWithType読み込みフィードバックの表示に適しています。continue userActivityビュー コントローラーを構築し、状態を復元するために使用されます。didFailToContinueUserActivityWithTypeハンドオフ失敗シナリオに使用されます。クイック ノートはこの失敗メソッドを呼び出しませんが、NSUserActivity に完全に接続されている場合に実装できます。

import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    var window: UIWindow?

    func scene(_ scene: UIScene, willContinueUserActivityWithType userActivityType: String) {
        // show user feedback while waiting for the NSUserActivity to arrive
    }

    func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
        // set up view controllers and views to continue the activity
    }

    func scene(_ scene: UIScene, didFailToContinueUserActivityWithType userActivityType: String, error: Error) {
        // show error about failing to continue an activity
    }
}

キーポイント:

  • SceneDelegate成し遂げるUIWindowSceneDelegate、シーンレベルのアクティビティ再開イベントを受信します。
  • windowインターフェイスを復元するときに使用できるウィンドウの入り口です。
  • willContinueUserActivityWithTypeアクティビティ エンティティが到着して読み込みプロンプトを表示する前にトリガーされます。
  • continue userActivityこれは、クイック ノート リンクを開いた後のメインの回復入り口です。
  • didFailToContinueUserActivityWithType回復失敗のフィードバックを処理し、主にハンドオフを処理します。

4. macOS で同じアクティビティのセットを処理する

08:04NSUserActivityクロスプラットフォーム API です。 macOS アプリケーションは、アプリ デリゲートに対応するメソッドを実装します。プロセスは iOS と同じです。つまり、差し迫った復元の通知を受信し、アクティビティを取得した後にドキュメントまたはビューを復元し、失敗した場合にプロンプ​​トを表示します。

import AppKit

class AppDelegate: NSObject, NSApplicationDelegate {
    func application(_ application: NSApplication, willContinueUserActivityWithType userActivityType: String) -> Bool {
        // show user feedback while waiting for the NSUserActivity to arrive
        return true
    }

    func application(_ application: NSApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([NSUserActivityRestoring]) -> Void) -> Bool {
        // set up view controllers or documents to continue the activity
        return true
    }

    func application(_ application: NSApplication, didFailToContinueUserActivityWithType userActivityType: String, error: Error) {
        // show error about failing to continue an activity, if appropriate
    }
}

キーポイント:

  • import AppKitmacOS アプリケーションのライフサイクル タイプを提供します。
  • AppDelegate成し遂げるNSApplicationDelegate、アプリケーションレベルのアクティビティ再開イベントを受け取ります。
  • willContinueUserActivityWithType戻るtrue、アプリケーションがアクティビティを続行する意思があることを示します。
  • continue userActivity完全なアクティビティを受信したら、ドキュメントを開くか、ターゲット ビューに切り替えることができます。
  • restorationHandlerこれはシステム回復プロセスの一部であり、回復に参加しているオブジェクトを返すことができます。
  • didFailToContinueUserActivityWithTypeリカバリが失敗した場合のエラー処理を担当します。

5. 頻繁に userInfo を書き込むのを避けるために、needsSave を使用します。

(12:18) 一部の回復ステータスは急速に変化します。地図の中心点とズームスケールはジェスチャーに応じて継続的に変化します。すべてのジェスチャを書き直すuserInfo追加のオーバーヘッドが発生します。 Apple推奨の設定needsSave = true、システムがアクティビティをクイック ノートまたはハンドオフに送信するまで待ってから、userActivityWillSave最新のステータスをコールバックに書き込みます。

import UIKit

class MapViewController: UIViewController {
    @IBOutlet var scrollView: UIScrollView!
    var visibleFrame: CGRect = .zero

    func markActivityForSaving(activity: NSUserActivity) {
        activity.needsSave = true
    }

    override func userActivityWillSave(_ userActivity: NSUserActivity) {
        userActivity.userInfo = [
            "center": NSStringFromCGPoint(CGPoint(x: visibleFrame.midX, y: visibleFrame.midY)),
            "zoomScale": scrollView.zoomScale
        ]
    }
}

キーポイント:

  • MapViewController表示領域とズーム率を保存する必要があるインターフェイスを表します。
  • scrollView現在のズームレベルを提供します。
  • visibleFrame現在表示されている領域を提供します。
  • markActivityForSaving置くだけneedsSaveとしてマークされていますtrue、各ジェスチャで状態辞書が書き換えられるのを避けるため。
  • userActivityWillSaveシステムがアクティビティを保存する必要がある前に呼び出されます。
  • userInfo書くcenterそしてzoomScale、回復時に同じ視野に戻ることができます。
  • NSStringFromCGPoint中心点を挿入可能に変換しますuserInfo弦。

6. 古いリンクや欠落しているコンテンツとの互換性

(13:08) クイック ノートのリンクは、アプリの古いバージョンまたは新しいバージョンから取得されている場合があります。ユーザーが保存したコンテンツも削除または移動された可能性があります。アクティビティを復元するときは、欠落しているフィールド、不明なフィールド、存在しないコンテンツを処理できる必要があります。

import Foundation

enum ActivityRestoreResult {
    case open(identifier: String)
    case showDeletedMessage
    case ignoreNewerVersion
}

func restoreResult(from userActivity: NSUserActivity, currentSupportedVersion: Int) -> ActivityRestoreResult? {
    let info = userActivity.userInfo ?? [:]
    let activityVersion = info["activityVersion"] as? Int ?? 1

    guard activityVersion <= currentSupportedVersion else {
        return .ignoreNewerVersion
    }

    guard let identifier = info["documentIdentifier"] as? String else {
        return nil
    }

    guard documentStillExists(identifier) else {
        return .showDeletedMessage
    }

    return .open(identifier: identifier)
}

func documentStillExists(_ identifier: String) -> Bool {
    true
}

キーポイント:

  • ActivityRestoreResult回復結果を開いたコンテンツに分割し、削除プロンプトを表示し、新しいバージョンのリンクを無視します。
  • restoreResultからuserInfoリカバリに必要なフィールドを読み取ります。
  • activityVersionデフォルト値は次のとおりです1、バージョン フィールドのない以前のリンクとの互換性を容易にするため。
  • guard activityVersion <= currentSupportedVersion新しい形式のリンクに遭遇したときに、古いアプリケーションが正常に失敗するようにします。
  • documentIdentifierこれはコンテンツを開くためのキー フィールドであり、不足している場合は返されます。nil
  • documentStillExistsアプリケーション自身のコンテンツの存在チェックを表します。
  • コンテンツが削除された場合に戻る.showDeletedMessage、コンテンツを移動するときにここでリダイレクト ロジックを変更できます。

重要ポイント

  • 何をすべきか: 読書アプリケーションに「ノートバウンス」機能を追加します。 実行する価値がある理由: クイック ノートを使用すると、現在の記事をユーザーのメモにバインドでき、ユーザーは記事を再読するときにメモの提案が表示されます。 開始方法: 記事詳細ページを作成するNSUserActivity、記事の UUID で設定されますtargetContentIdentifier、シーンデリゲートのcontinue userActivityで記事を開きます。

  • やるべきこと: 場所のメモを地図または旅行アプリに保存します。 実行する価値がある理由: ルートを計画するときに、ユーザーは場所のリンクをクイック ノートに追加し、場所のページに戻って表示を続けることができます。 開始方法: 場所のグローバル ID を使用して設定しますpersistentIdentifier、地図の中心点とズーム率を入力します。userInfo、使用needsSaveビューステートの更新を遅らせます。

  • 内容: ドキュメント エディターにクロスデバイスのメモ リンクのサポートを提供します。 実行する価値がある理由:NSUserActivityクイック ノートとハンドオフを同時に提供することで、ユーザーは iPad でドキュメントのメモを記録した後、Mac で同じドキュメントに戻ることができます。 開始方法:NSUserActivityTypesドキュメントビューアクティビティタイプを登録し、ドキュメントIDを書き込みますtargetContentIdentifier、macOS側の実装application(_:continue:restorationHandler:)

  • 対処方法: 製品詳細ページにクイック ノートの返品エントリを追加します。 実行する価値がある理由: ユーザーは製品を比較するときに、メモに複数のリンクを保存します。安定した識別子により、古いメモでも引き続き正しい製品を開くことができます。 開始方法: 製品の背景 ID を安定した値として使用し、設定しますactivity.title商品のタイトルです。コンテンツが棚から削除されると、回復プロセス中に明確なプロンプトが表示されます。

  • 内容: Web サイトのサポートを備えたアプリケーションに Web フォールバックを提供します。 実行する価値がある理由:webpageURLこれにより、アプリがインストールされていないデバイスでも Web ページを開くことができ、リンクは依然としてどこかに移動します。 開始方法: 設定内targetContentIdentifierまたはpersistentIdentifier同時に設定するwebpageURL、URL にセッション ID や一時的なフィルタリング ステータスが含まれていないことを確認します。

関連セッション

コメント

GitHub Issues · utterances