WWDC Quick Look 💓 By SwiftGGTeam
Get started with Writing Tools

Get started with Writing Tools

元の動画を見る

ハイライト

iOS 18 で導入された Writing Tools は、任意のテキストビューでテキストの校正、書き換え、変換を可能にします。開発者は UITextView/NSTextView(TextKit 2)または WKWebView を使うだけで完全サポートを得られます(00:14)。

主要内容

ユーザーがアプリ内でテキストを編集し、文法を校正したり表現を書き換えたい場合、通常はアプリを離れ外部ツールを使い、内容を貼り戻す必要があります——フローが分断され、書式が失われやすい。Apple は iOS 18、macOS 15 で Writing Tools を導入し、校正、書き換え、要約、リスト変換、テーブル変換をネイティブテキストビューに統合しました。テキスト選択後、編集メニュー、フローティングバー、コンテキストメニューから直接呼び出せます。提案はインライン表示され、スタイル、リンク、添付などのリッチテキスト属性は書き換え後も自動保持されます(03:39)。

開発者にとって Writing Tools はすぐ使えます。UITextView、NSTextView(TextKit 2 必須)、または WKWebView を使えば、システムが自動的にエントリポイントを公開します。テキスト処理時、システムはより良い結果のために選択範囲を完全な文まで自動拡張し、attributed string で元の属性を維持します。開発者は新しいデリゲートメソッドで同期を一時停止し、特定のテキスト範囲(コードブロック、引用など)を保護でき、アプリ状態とテキスト操作の一貫性を保てます(05:28)。

詳細

デフォルト動作と TextKit バージョンの違い

UITextView または NSTextView 使用時、Writing Tools の体験は TextKit バージョンに依存します。TextKit 2 は完全な体験——提案をインライン表示し、テキストビュー内で直接プレビューと変更適用が可能。TextKit 1 はパネルモードに退化し、処理結果のみ表示(03:58)。WKWebView のデフォルトは .limited(パネルモード)。完全な体験には WKWebViewConfiguration で明示的に .complete を設定(08:06)。

Writing Tools セッションの監視

Writing Tools がアクティブな間、text storage を直接変更します。アプリは同時にテキストの永続化や同期を避けるべきです。Apple はセッションライフサイクルを監視する新しいデリゲートメソッドを提供(05:28):

func textViewWritingToolsWillBegin(_ textView: UITextView) {
    // Take necessary steps to prepare. For example, disable iCloud sync.
}

func textViewWritingToolsDidEnd(_ textView: UITextView) {
    // Take necessary steps to recover. For example, reenable iCloud sync.
}

if !textView.isWritingToolsActive {
    // Do work that needs to be avoided when Writing Tools is interacting with text view
    // For example, in the textViewDidChange callback, app may want to avoid certain things
    // when Writing Tools is active
}
  • textViewWritingToolsWillBegin: セッション開始時に呼ばれる。自動保存、テキスト分析など text storage を妨げる操作の一時停止に適する
  • textViewWritingToolsDidEnd: セッション終了時に呼ばれる。一時停止した状態を復元
  • isWritingToolsActive: 読み取り専用プロパティ。コールバックで Writing Tools セッション進行中か判定

動作制御と受け入れフォーマット

writingToolsBehavior で体験モードを制御(07:11):

textView.writingToolsBehavior = .limited  // パネルモードのみ
textView.writingToolsBehavior = .none     // 完全に無効化

writingToolsAllowedInputOptions でテキストビューが受け入れるフォーマットタイプを指定(07:31):

textView.writingToolsAllowedInputOptions = [.plainText]
textView.writingToolsAllowedInputOptions = [.plainText, .richText, .table]
  • .plainText: プレーンテキストのみ受け入れ
  • .richText: リッチテキスト(スタイル、リンク、添付を含む)を受け入れ
  • .table: テーブル変換を受け入れ(テキストビューが NSTextTable をサポートする必要あり)

デフォルトではビューはプレーンテキストとリッチテキストをサポートするがテーブルは非サポートと仮定。テーブルをレンダリングできる場合は明示的に宣言。

WKWebView の設定

WKWebView の Writing Tools API は WKWebViewConfiguration 経由で設定。デフォルト動作は .limitedWKWebView 自体も isWritingToolsActive プロパティでセッション状態を確認可能(07:55):

// For `WKWebView`, the `default` behavior is equivalent to `.limited`
extension WKWebViewConfiguration {
    @available(iOS 18.0, *)
    open var writingToolsBehavior: UIWritingToolsBehavior { get set }
}

extension WKWebViewConfiguration {
    @available(macOS 15.0, *)
    open var writingToolsBehavior: NSWritingToolsBehavior { get set }
}

extension WKWebView {
    /// If the Writing Tools behavior on the configuration is `.limited`, this will always be `false`.
    @available(iOS 18.0, macOS 15.0, *)
    open var isWritingToolsActive: Bool { get }
}
  • writingToolsBehavior: WKWebViewConfiguration 上で設定。WKWebView の Writing Tools 体験レベルを決定
  • isWritingToolsActive: 動作が .limited の場合は常に false。完全体験でのみアクティブセッションを検出可能

特定テキスト範囲の保護

ノートアプリのコードブロック、メールアプリの引用コンテンツは書き換えるべきではありません。新しいデリゲートメソッドで無視する NSRange を返すと、Writing Tools はこれらの範囲に修正提案をしません(08:48):

// Returned `NSRange`s are relative to the substring of the textView's textStorage from `enclosingRange`
func textView(_ textView: UITextView, writingToolsIgnoredRangesIn
        enclosingRange: NSRange) -> [NSRange] {
    let text = textView.textStorage.attributedSubstring(from: enclosingRange)
    return rangesInappropriateForWritingTools(in: text)
}
  • enclosingRange: システムが渡す処理範囲。返す NSRange はこの範囲の部分文字列に対する相対位置
  • attributedSubstring: textStorage から部分文字列を取り出し、コードブロックや引用を検出
  • WKWebView は <blockquote><pre> タグ内のコンテンツを自動的に無視(08:56

カスタムテキストビューのサポート

UITextView/NSTextView 以外のカスタムテキストビューでは、iOS/iPadOS 上で UITextInteraction を採用すれば Writing Tools メニュー項目を無料で取得。システムは UITextInput プロトコル経由でテキストの読み書きとポップオーバー配置を行い、新しい isEditable プロパティでビューが編集可能かを示します(09:58):

protocol UITextInput {
    @available(iOS 18.0, macOS 15.0, *)
    optional var isEditable: Bool { get }
}

macOS では、カスタムビューは NSServicesMenuRequestor プロトコルを実装し validRequestor(forSendType:returnType:) をオーバーライドし、ビューにコンテキストメニューを追加すれば Writing Tools メニュー項目を自動取得(11:05):

class CustomTextView: NSView, NSServicesMenuRequestor {
    override func validRequestor(forSendType sendType: NSPasteboard.PasteboardType?,
                                 returnType: NSPasteboard.PasteboardType?) -> Any? {
        if sendType == .string || sendType == .rtf {
            return self
        }
        return super.validRequestor(forSendType: sendType, returnType: returnType)
    }

    nonisolated func writeSelection(to pboard: NSPasteboard,
                                    types: [NSPasteboard.PasteboardType]) -> Bool {
        // Write plain text and/or rich text to pasteboard
        return true
    }

    // Implement readSelection(from pboard: NSPasteboard)
    // as well for editable view
}
  • validRequestor: ビューがサポートするペーストボードタイプ(プレーンテキスト .string、リッチテキスト .rtf など)を宣言。self を返すとビューが処理可能
  • writeSelection: 選択内容をペーストボードに書き込み、システムが読み取る
  • readSelection: ペーストボードから処理済みテキストを読み取りビューに適用(編集可能ビューでは必須)

重要ポイント

  • Writing Tools セッション監視を実装: textViewWritingToolsWillBegin で自動保存と同期を一時停止、textViewWritingToolsDidEnd で復元。主要コールバックで isWritingToolsActive を確認。
    • 価値がある理由: Writing Tools アクティブ中は text storage を直接変更。同時に自動保存やクラウド同期を行うと競合とデータ不整合が起きる。
    • 始め方: UITextViewDelegate/NSTextViewDelegate で 2 つの新デリゲートメソッドを実装し、一時停止が必要なロジックを集中管理。
  • 書き換えるべきでないテキスト範囲を保護: writingToolsIgnoredRangesIn でコードブロック、引用、固定用語などの NSRange を返し、Writing Tools の修正提案を阻止。
    • 価値がある理由: ユーザーが段落全体を校正する際、コードブロックや引用が書き換えられると元の意味が壊れ、混乱と追加修正が発生。
    • 始め方: delegate で textView(_:writingToolsIgnoredRangesIn:) を実装し、テキスト属性を解析して無視範囲を返す。WKWebView プロジェクトは追加コード不要——<blockquote><pre> タグは自動無視。
  • WKWebView で完全体験を明示的に有効化: WKWebViewConfigurationwritingToolsBehavior.complete に設定。WebView 内テキストエディタがインラインプレビューと直接適用を得られる。
    • 価値がある理由: WKWebView のデフォルトは .limited(パネルモード)。結果を見て手動コピペする必要があり、ネイティブテキストビューのインライン操作より体験が明らかに劣る。
    • 始め方: WKWebViewConfiguration 作成時に writingToolsBehavior = .complete を設定し、isWritingToolsActive でセッション状態を確認。
  • テキストビューがサポートする入力フォーマットを宣言: カスタムテキストビューがテーブルをレンダリングできる場合、writingToolsAllowedInputOptions.table を明示的に含める。プレーンテキストのみなら .plainText のみ宣言。
    • 価値がある理由: システムはデフォルトでテーブル非サポートと仮定。レンダリング能力があるのに宣言しないと、Writing Tools のテーブル変換が使えない。
    • 始め方: テキストビュー初期化時に writingToolsAllowedInputOptions = [.plainText, .richText, .table] を設定。実際のレンダリング能力に応じてサブセットを選択。

関連セッション

コメント

GitHub Issues · utterances