TextKit2 を利用して縦書きレイアウトを行う
今年の WWDC22 では大きな取り沙汰はありませんでしたがシステムフレームワークへは順調に TextKit2 への置き換えが進んでいるようです
そこで今回は理解を兼ねて TextKit2 を利用してテキストを縦書きレイアウトを行う一例を紹介します
内容やコードは WWDC21 の Meet TextKit 2 を参考にしているのでそちらも確認すると理解が深まると思います
(上記セッションのサンプルコードの挙動が少しおかしかったり実装が分かりづらかったのが本記事執筆のきっかけです)
完成形は以下の通りです
はじめに
まず前提として NSTextLayoutManager
には現状レイアウト方向を指定する方法がない?ためビュー側で吸収する必要があります
また今回は表示のみの機能のため単純な対応になっています
縦書きに必要な処理
といっても大仰なことは必要なくテキスト全体に verticalGlyphForm
を指定することだけです
string.addAttribute(.verticalGlyphForm, value: true, range: NSRange(location: 0, length: string.length))
余談ですが、 TextKit1 では verticalGlyphForm
だけでは実現できず、 Core Text を駆使する必要がありました
ビュー側の対応
ここまでで下記のような結果になります
NSTextLayoutFragment
が文章ブロック毎の縦に積まれた座標を持っているので、ここを 90 度回転させて横方向に並べれば縦書きレイアウトが達成できそうです
が、今回はその外側のスクロールビュー自体を 90 度回転させて対応しました
contentView.frame = bounds
contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
+ contentView.transform = .init(rotationAngle: .pi / 2)
変更範囲は上記の一箇所だけでよく、ビューポート範囲の矩形に関しても transform
が適用された状態になるので、そのままで問題ありません
func viewportBounds(for textViewportLayoutController: NSTextViewportLayoutController) -> CGRect {
CGRect(origin: contentOffset, size: bounds.size)
.insetBy(dx: 0, dy: -100)
}
おわりに
感想としてはテキスト機能の 編集・選択・表示 のうち表示に限っていえば TextKit2 を用いると、とても簡単に縦書きを実現することができました
(当初は備忘録として記録を残すために書き始めたのですが、逆に書くことがなくて悩みました…)
また NSTextViewportLayoutController
のおかけで大きなテキストでも充分にパフォーマンスが発揮されていて色々なものに活用できそうです
Discussion
とても参考になります。ありがとうございます。
TextKit 2を利用した場合、ページに分割して表示することは可能でしょうか?
「ページに分割して表示」がどういった振る舞いを期待しているのかわかりませんが、 TextKit2 に限らず
UIScrollView.isPagingEnabled
はいかがでしょうか?もしくは
NSTextLayoutFragment
がCGRect
を持っているので、それを利用すれば分割できそうです