1755468177
2025-08-15 21:26:00
TextKit 2(nstextlayoutmanager)APIはそうでした 発表 4年以上前のWWDC21の間に公開されています。それ以前は、数年間民間発展に陥り、MacOSおよびiOSフレームワークで広く採用されていました。老化したTextKit 1を置き換えるより簡単で、より速く、全体的に優れたAPIおよびテキストレイアウトエンジンを約束しました(nslayoutmanager)エンジン。
長年にわたり、私はTextKit 2とMACOS/IOSテキスト処理のある程度の専門知識を得ていました。 sttextView -macos(appkit)とiOS(uikit)のtextViewの再実装は、テキストレイアウトエンジンとしてTextKit 2フレームワークを使用しています。 人前で話す賛美 新しい、より良いエンジンは、すべての問題を解決する必要があります。
4年間の経験に基づいて、私はtrapに落ちたように感じます。銀の弾丸ではありません。それは間違いなくTextKitよりも改善されています。1。職務に適したツールではなく(最悪の場合)、TextKit 2を(せいぜい)使用するのに迷惑にする特定の問題について説明したいと思います。
アーキテクチャと実装
TextKit2アーキテクチャは良いです。抽象化とコンポーネントは、非常に理にかなっており、進歩的な複雑さの前提を実現します。しかし、実装はアーキテクチャと同等ではありません。片側では、 nstextcontentmanager レイアウトエンジンの抽象インターフェイスを提供します。 実際に、以外のものを使用します nstextcontentStorage 不可能です。 nstextContentStorageは、機能するストレージの実装を提供する(および唯一の)ものです。それ自体が裏付けられています nstextStorage、これは、コンテンツストレージ自体の抽象インターフェイスです。これは、nStextStorageがTextKit 2にも適用される可能性のあるすべての問題を意味します。要するに、 uitextview/nstextViewは、nStextContentStorage以外のものでは機能しません。
テキストコンテンツマネージャーは一連のシリーズで動作します nstextelement ブロックですが、繰り返しますが、唯一の作業実装はから継承する必要があります nstextparagraph、またはあなたはトラブルに陥っています(ランタイムアサーション)。
実装は一貫性がなく、意図的なようです。 TextKit2はuitextviewで使用するように実装されており、それはすぐに明らかです。そうでなければそうだったかもしれない素晴らしいアイデアのなんて無駄です。
ソフトウェアのバグが予想されており、TextKit 2の場合も例外ではありません。 私は多くのバグを自分で報告しました。一部の問題は修正されていますが、他の問題は未解決のままです。多くのユーザーは応答を受けませんでした。さらに、バグは特定のバージョンで発生し、回帰が一般的です。もちろん、互換性を維持するのは面倒です。私の観点からは、おそらく最も迷惑です バグは「エクストララインフラグメント」の周りにあります (ドキュメントの最後にある余分な線フラグメントの長方形とその壊れたレイアウト。
ビューポートは闘争です
しかし、本当の闘争は、ビューポートの新しく紹介されたアイデアとそれがどのように機能するかについてです。 ViewPortは、テキストレイアウトエンジンの作業を最適化し、ドキュメント全体ではなく目に見える領域に焦点を当てることにより、メモリフットプリントを最小限に抑えるツールです。ビューポートは、ユーザーがドキュメントのさまざまな部分と対話するときに「移動」する目に見える領域のごく一部です(たとえば、ビューポートフレームが動きます)
Viewportの約束は、ドキュメントのランダムなフラグメントのレイアウトを取得するためにドキュメント全体のレイアウトを確認する必要がないということです。この機能を機能させるには、さまざまなキャッシュ、間隔の管理、範囲の無効化、およびその他の関連タスクが必要です。 TextKit 2フレームワークはそのすべてを処理します。
これが段階です。テキストが入ったウィンドウがあると想像してください。テキストは上下にスクロールします。スクロールすると、可視領域にレイアウトテキストが表示されます。したがって、典型的なテキストエディター/ビューアーシナリオ。

ビューポート管理の問題の1つは、ビューポートの機能とまったく同じものです。ビューポート(可視領域)でのみレイアウトを確保する場合、ドキュメントの他のすべての部分が推定されます。具体的には、ドキュメントの総高さが推定されます。私がドキュメントのより多くの/異なる部分をレイアウトすると、推定は頻繁に変化します。それは、スクロールアップ/ダウン中にビューポートを移動するときに起こります。 TextKitはの値を更新します nstextlayoutmanager.usageboundsfortextcontainer 見積もりが変更されるたびに。ドキュメントの総高さを推定するためのレシピは
- ensureLayout(for:documentrange.endlocation) つまり、ドキュメント全体のレイアウトを強制せずに、ドキュメントの終了のレイアウトを確認します。その操作は、定義上、推定サイズになります。
- 一致するようにビューをサイズ変更します usageBoundStorextContainer 価値。 scrollviewで、これにより スクロラー 現在のドキュメントの位置を反映します。
このアプローチで私が気付く最初の問題は、ドキュメントをスクロールして、変化するビューポートの位置をレイアウトし続けるとき、usageboundsfortextcontainerの価値は 不安定。頻繁に価値が大きく変化します。 ScrollViewでは、高さに頻繁かつ大幅な変化が発生します。

揺れは非常に迷惑で、受け入れるのが難しいです。高さが推定されることを考えると、これも予想されます。設計されている作業:
これは正しく、設計されています。TextKit2のビューポートベースのレイアウトでは、ドキュメントが完全にレイアウトされる必要はありません。画面に表示されるテキストの一部がレイアウトされる必要があります。これは、より良いスクロールパフォーマンスを実現する方法です。

より安定した値として(私の観察から)わずかに「より良い」、私は最後の「レイアウト要素」の場所を要求するときに受け取ります。 enumerateTextLayoutFragments 最後のレイアウトフレームを要求し、最後のフラグメントのみを要求します。
enumerateTextLayoutFragments(from: documentRange.endLocation, options: [.reverse, .ensuresLayout]) {
layoutFragment in lastLineMaxY = layoutFragment.layoutFragmentFrame.maxY
return false
}
その見積もりも単なる推定であり、通常、値は最終的な完全にレイアウトされたドキュメントよりも大幅に高くなります。ドキュメントの最後にジャンプするにはどうすればよいですか?答えは次のとおりです。
- 推定(大きすぎるまたは小さすぎる)コンテンツの高さを受け取る
- 推定された高さでビューコンテンツサイズを更新します
- ドキュメントの最後にレイアウトを施行します
- その高さの終わりまでビューポートを移動(移動)します(最終または推定)
そして、はい、ビューポートにはドキュメントの終わりが表示されますが、コンテンツの総高さはまだ推定されています。つまり、スクロラーは間違った位置にある可能性が最も高くなります(間違っています)。それに対する「修正」とは何ですか?最良の推測は、人為的かつ継続的に「ビューポートの位置」を「調整」することです。それでも、私たちはその事実を無視し、その事実(コンテキストから)を認識し、その位置がドキュメントサイズの範囲外であっても、その位置でドキュメントの終わりを表示するためにビューポートを「偽造」します。その操作(このような調整が必要です)は脆弱であり、率直に言って、目立たない方法で扱うのは簡単ではありません。
長い間、私は「間違っている」と思っていたので、これらの問題に対処する方法(おそらくプライベートAPI)があるに違いありません。 MacOSのTextEditアプリは、実装で行うのとまったく同じ問題に苦しんでいます。
TextEditとTextKit 2グリッチ。ボタンを押す場所を知っている場合。
TextEditとTextKit 2グリッチ。ボタンを押す場所を知っている場合。
だから、だから
今日、私はそうではないと思います。 TextKit 2 APIとその実装は不足しており、予想外に正しく使用することが困難です。設計はしっかりしていますが、実際のアプリケーションに適用することは困難であることがわかりました。私は自分の発見のより良いまたはより楽観的な要約を持っていたらいいのにと思いますが、それがそうです。特にテキスト編集UIに関しては、TextKit 2はテキストレイアウトに最適なツールではないと考え始めました。私は提案を受け入れたままであり、うまくいけば、ユーザーエクスペリエンスを損なうことなくTextKit 2を使用する方法を見つけることができます。
#TextKit #2約束された土地
