ハイライト
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 経由で設定。デフォルト動作は .limited。WKWebView 自体も 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 で完全体験を明示的に有効化:
WKWebViewConfigurationでwritingToolsBehaviorを.completeに設定。WebView 内テキストエディタがインラインプレビューと直接適用を得られる。- 価値がある理由: WKWebView のデフォルトは
.limited(パネルモード)。結果を見て手動コピペする必要があり、ネイティブテキストビューのインライン操作より体験が明らかに劣る。 - 始め方:
WKWebViewConfiguration作成時にwritingToolsBehavior = .completeを設定し、isWritingToolsActiveでセッション状態を確認。
- 価値がある理由: WKWebView のデフォルトは
- テキストビューがサポートする入力フォーマットを宣言: カスタムテキストビューがテーブルをレンダリングできる場合、
writingToolsAllowedInputOptionsに.tableを明示的に含める。プレーンテキストのみなら.plainTextのみ宣言。- 価値がある理由: システムはデフォルトでテーブル非サポートと仮定。レンダリング能力があるのに宣言しないと、Writing Tools のテーブル変換が使えない。
- 始め方: テキストビュー初期化時に
writingToolsAllowedInputOptions = [.plainText, .richText, .table]を設定。実際のレンダリング能力に応じてサブセットを選択。
関連セッション
- What’s new in UIKit — UIKit のテキストと入力関連の新变化、Writing Tools のシステムレベル統合を含む
- What’s new in AppKit — macOS 上の Writing Tools と NSServicesMenuRequestor の統合詳細
- Design App Intents for system experiences — App Intents がシステムレベル体験をどう駆動するか。Writing Tools と補完関係
- Build multilingual-ready apps — テキスト入力と多言語シーンのベストプラクティス。Writing Tools と組み合わせて国際化体験を向上
コメント
GitHub Issues · utterances