UIView
多数の個別のNSLayoutManager
オブジェクトを使用してテキストをレンダリングするカスタムを開発しています。(このビューには、組み込みのテキスト ビュー クラスがサポートできる以上のテキスト配置要件があります。) レイアウトされているテキストがヘブライ語の場合、レイアウトの 1 つの正しい垂直位置を取得するのに苦労しています。この投稿が長くなってしまったことをお詫びしますが、できるだけ関連する詳細を含めたかったのです。
問題
これが私が達成しようとしていることの例です:
"2" のベースラインは、ヘブライ文字 "ek" (カスタム フォントで表示) のベースラインとうまく一致しています。ただし、「2」のテキストをヘブライ数字 (実際には、数値コンテキストで値 2 を持つヘブライ文字「ב」) に切り替えると、次のようになります (「ב」が高すぎる場合)。
ここでの唯一の変更は、テキスト文字列です。ではなく、"2"
です"\u{05b1}"
。すべてが共通のベースラインにあることを望み、問題を修正するための提案をいただければ幸いです (これは だけでなく、すべてのヘブライ語の数字で発生します"\u{05b1}"
)。
コード
NSLayoutManager
(技術的な理由から、数字と本文の両方に1 つの数字を使用することはできません。) 数字のレンダリング位置をフォントのアセントの違いだけ垂直方向にシフトすることで、ベースラインを揃えようとしています。シフトを計算するコードは次のとおりです。
numberFont = UIFont.systemFont(ofSize: /* some size */)
offset = fullFont.ascender - numberFont.ascender
NSLayoutManager
番号の作成方法は次のとおりです。
var lbl = "2" // or "\u{05b1}"
let paraStyle = NSMutableParagraphStyle()
paraStyle.alignment = .center
let text = NSTextStorage(
string: lbl,
attributes: [NSFontAttributeName: numberFont,
NSParagraphStyleAttributeName: paraStyle]
)
let layout = NSLayoutManager()
text.addLayoutManager(layout)
let container = NSTextContainer(
size: CGSize(width: /* some fixed width */, height: bounds.height - offset)
)
container.lineFragmentPadding = 0
layout.addTextContainer(container)
let x = ... // irrelevant
let origin = CGPoint(x: x, y: bounds.minY + offset)
後で、次を使用してテキストをレンダリングします。
let range = layout.glyphRange(for: layout.textContainers[0])
layout.drawGlyphs(forGlyphRange: range, at: origin)
本文は同じロジックを使用して処理されますが、ロジックは使用されませんoffset
(もちろんフォントは異なります)。これは、アラビア数字 (例: "2") ではうまく機能しますが、ヘブライ数字 (例: "ב") では失敗します。
さらに詳しい情報
上の画像の場合、フォント サイズ ( .pointSize
) とアセント ( .ascender
) のプロパティは次のとおりです。
サイズ: テキスト = 14.3890409469604 ; 数 = 8.63342465753425
上昇: テキスト = 16.159567469731 ; 番号 = 8.2203017979452
また、レイアウトに関するいくつかの診断情報も生成しますが、その数値はあまり意味がありません。情報を出力するコードは次のとおりです。
size = text.size()
mgr = NSLayoutManager()
text.addLayoutManager(mgr)
container = NSTextContainer(
size: CGSize( width: CGFloat.greatestFiniteMagnitude,
height: CGFloat.greatestFiniteMagnitude))
container.lineFragmentPadding = 0
mgr.addTextContainer(container)
mgr.ensureLayout(for: container)
range = mgr.glyphRange(for: container)
print("label:", lbl)
print(" text size:", size)
print(" bounds:", mgr.boundingRect(forGlyphRange: range, in: container))
2 つのテキスト ("2" と "\u{05d1}") の結果は次のとおりです。
label: 2
text size: (5.3958904109589, 10.3027782534247)
bounds: (0.0, 0.0, 5.3958904109589, 10.3027782534247)
label: ב
text size: (4.67931616438356, 9.19459726027397)
bounds: (-1.88208657534247, -0.578439452054793, 9.5054005479452, 9.77303671232876)
このパズルについていくつかのことが私を困惑させます:
- ヘブライ語のテキストの境界は、x と y の負の値から始まります。負の x 原点は、ヘブライ語が右から左へのスクリプトであることと関係があるかもしれませんが、負の y 原点はどうですか?
- 報告されるレイアウトのサイズは、境界の高さと境界の y 開始点、または (おそらくより正確には) コンテナー内のテキストの下部の代数的合計であり、実際の全体のサイズではありません。
NSAttributedString.size()
壊れていますか? - 「2」と「\u{05d1}」の垂直位置の視覚的な違いを説明する数字はないようです。