Ios 对齐NSLayoutManager的基线时出现问题

Ios 对齐NSLayoutManager的基线时出现问题,ios,uiview,textkit,nslayoutmanager,nstextcontainer,Ios,Uiview,Textkit,Nslayoutmanager,Nstextcontainer,我正在开发一个自定义的UIView,它使用许多单独的NSLayoutManager对象呈现文本。(此视图的文本定位要求超出了内置文本视图类所能支持的范围。)当所布局的文本是希伯来文时,我很难获得其中一个布局的正确垂直位置。我为这篇文章的篇幅感到抱歉,但我希望尽可能多地包含相关细节 问题 以下是我试图实现的一个示例: “2”的基线与希伯来文字母“ו”(以自定义字体呈现)的基线对齐。但是,如果我将“2”的文本转换为希伯来语数字(实际上是希伯来语字母“ב”,在数字上下文中其值为2),结果如下(其中“

我正在开发一个自定义的
UIView
,它使用许多单独的
NSLayoutManager
对象呈现文本。(此视图的文本定位要求超出了内置文本视图类所能支持的范围。)当所布局的文本是希伯来文时,我很难获得其中一个布局的正确垂直位置。我为这篇文章的篇幅感到抱歉,但我希望尽可能多地包含相关细节

问题

以下是我试图实现的一个示例:

“2”的基线与希伯来文字母“ו”(以自定义字体呈现)的基线对齐。但是,如果我将“2”的文本转换为希伯来语数字(实际上是希伯来语字母“ב”,在数字上下文中其值为2),结果如下(其中“ב”太高):

这里唯一的更改是文本字符串。它不是
“2”
,而是
“\u{05b1}”
。我希望一切都在一个共同的基线上,并希望有任何解决问题的建议(这适用于所有希伯来数字,而不仅仅是
“\u{05b1}”

代码

(我应该提到,出于技术原因,我不能对数字和主文本使用一个
NSLayoutManager
)我正试图通过垂直移动数字的渲染位置来对齐基线,方法是根据字体升幅的差异。以下是计算移位的代码:

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)
主文本使用相同的逻辑处理,但没有偏移量(当然还有不同的字体)。这对阿拉伯数字(如“2”)很有效,但对希伯来数字(如“ב”)无效

进一步信息

对于上面的图像,字体大小(
.pointSize
)和上升(
.ascender
)属性是:

尺寸:文本=14.3890409469604;数字=8.63342465753425
上升:text=16.159567469731;数字=8.22030179452

我还生成了一些关于布局的诊断信息,但是这些数字对我来说没有多大意义。以下是打印信息的代码:

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”和“\u{05d1}”)的结果:

标签:2
文本大小:(5.3958904109589,10.3027782534247)
界限:(0.0,0.0,5.3958904109589,10.3027782534247)
标签:ב
文本大小:(4.67931616438356,9.19459726027397)
界限:(-1.88208657534247,--0.578439452054793,9.5054005479452,9.77303671232876)

关于这件事,有几件事让我感到困惑:

  • 希伯来文文本的边界从x和y的负值开始。负x源可能与希伯来语是从右向左的脚本有关,但是负y源呢
  • 报告的布局大小是边界高度和边界的y起点或(可能更准确地说)容器中文本底部的代数和,而不是实际的总体大小。
    NSAttributedString.size()是否已损坏
  • 这些数字似乎都不能解释“2”和“\u{05d1}”垂直位置的视觉差异

如果将“2”和“\u{05d1}”放在同一个字符串中,相邻放置,会发生什么情况?@davewston-Aha!当我尝试这样做时,它将
“\u{05d1}”
降低到它应该在的位置。经过一段时间的反复思考,这导致了一个可接受的解决方案:在
“\u{05d1}”
之前添加一个零宽度的空格(
“\u{200b}”
)。虽然这解决了让我的屏幕看起来正确的问题,但我肯定想知道发生了什么,为什么这样的混乱是必要的。我喜欢拼图。你能组织一个小项目或操场来证明这个问题吗?我已经通读了有属性的字符串指南,但没有任何东西向我跳出来。