在 UILabel 的 draw#rect 中获取字形 boundingRect
Getting a glyph boundingRect in draw#rect in UILabel
使用 Swift,我想在 UILabel 的 draw#rect 中获取字形的 boundingRect。
UILabel 已经有大小(在示例中为 300x300)和质量,例如文本居中。
class RNDLabel: UILabel {
override func draw(_ rect: CGRect) {
let manager = NSLayoutManager()
let store = NSTextStorage(attributedString: NSAttributedString(
string: text!,
attributes: [NSAttributedString.Key.font: font]))
store.addLayoutManager(manager)
let textContainer = NSTextContainer(size: rect.size)
// note, intrinsicContentSize is identical there, no difference
manager.addTextContainer(textContainer)
let glyphRange = manager.glyphRange(
forCharacterRange: NSRange(location: 0, length: 1),
actualCharacterRange: nil)
let glyphRect = manager.boundingRect(
forGlyphRange: glyphRange, in: textContainer)
print("glyphRect \(glyphRect)")
...context?.addRect(glyphRect), context?.drawPath(using: .stroke)
super.draw(rect)
}
绿色方块不正确 - 它应该更像这些红色方块!
似乎存在一些问题:
我做的 layoutManager 应该具有 UILabel 的特性,例如 "centered text"? (不过,我相信您实际上不能直接访问 UILabel 的布局管理器?)
我们应该使用 CTLineGetOffsetForStringIndex 之类的东西吗?这甚至可能在 draw#rect
请注意,除了没有正确的偏移量外,绿色方框的高度似乎也不对。 (它看起来更像是一个普通的旧 intrinsicContentSize 而不是一个字形边界框。)
怎么办?
郑重声明,我的总体目标是根据实际字形框移动字形(当然 "y"、"X" 等不同)。但总的来说,有很多有用的理由来了解一个字形的盒子。
原来这道题的答案好像是:
事实上,不管是否令人惊讶,您基本上没有 访问 UILabel 中的字形信息。
实际上,您无法在 UILabel 中获得那些字形“实际形状”。
尤其是 RobN。指出,一项调查表明,在 UILabel 中,_drawTextInRect:baselineCalculationOnly 完成了工作,那是一大堆临时代码。
情况的总结似乎是 UILabel 简单地早于 NSLayoutManager / Core Text 并且(到目前为止)只是简单地不使用那些现代系统。
只有那些现代系统才能给你这个...
...可以访问 glyph-by-glyph 个形状。
你原来的问题有点毫无疑问,因为它说了两个完全相反的事情。
一方面你说UILabel.
另一方面,您使用 NSLayoutManager 和 NSTextStorage 以及 NSTextContainer 等术语 — TextKit 堆栈。
这些是相反的,因为 UILabel 不是用 TextKit 堆栈绘制的。所以你要求做的就像试图用磁铁吸起一张纸;磁铁确实能举起东西,但它们举起的是磁性材料,纸不是其中之一。可以肯定的是,UILabel 必须以某种确定性的方式进行绘制,但是 什么 这种方式是完全未知且不透明的,并且与 TextKit 无关。
另一方面,UITextView,或者您自己使用 TextKit 绘制的视图,确实使用 TextKit,现在所有 TextKit 工具都适用。因此,如果此 是 一个 UITextView(或您使用 TextKit 自己绘制的视图),那么您要执行的操作会简单得多。
这不需要对界面的外观做太多改变。可以使 UITextView 的行为很像 UILabel(使其不可编辑和不可滚动),但重要的区别是整个文本工具包堆栈是直接公开的。这就是我要开始的地方。我是那些更喜欢 使用 框架而不是 对抗 框架的人之一。
使用 Swift,我想在 UILabel 的 draw#rect 中获取字形的 boundingRect。
UILabel 已经有大小(在示例中为 300x300)和质量,例如文本居中。
class RNDLabel: UILabel {
override func draw(_ rect: CGRect) {
let manager = NSLayoutManager()
let store = NSTextStorage(attributedString: NSAttributedString(
string: text!,
attributes: [NSAttributedString.Key.font: font]))
store.addLayoutManager(manager)
let textContainer = NSTextContainer(size: rect.size)
// note, intrinsicContentSize is identical there, no difference
manager.addTextContainer(textContainer)
let glyphRange = manager.glyphRange(
forCharacterRange: NSRange(location: 0, length: 1),
actualCharacterRange: nil)
let glyphRect = manager.boundingRect(
forGlyphRange: glyphRange, in: textContainer)
print("glyphRect \(glyphRect)")
...context?.addRect(glyphRect), context?.drawPath(using: .stroke)
super.draw(rect)
}
绿色方块不正确 - 它应该更像这些红色方块!
似乎存在一些问题:
我做的 layoutManager 应该具有 UILabel 的特性,例如 "centered text"? (不过,我相信您实际上不能直接访问 UILabel 的布局管理器?)
我们应该使用 CTLineGetOffsetForStringIndex 之类的东西吗?这甚至可能在 draw#rect
请注意,除了没有正确的偏移量外,绿色方框的高度似乎也不对。 (它看起来更像是一个普通的旧 intrinsicContentSize 而不是一个字形边界框。)
怎么办?
郑重声明,我的总体目标是根据实际字形框移动字形(当然 "y"、"X" 等不同)。但总的来说,有很多有用的理由来了解一个字形的盒子。
原来这道题的答案好像是:
事实上,不管是否令人惊讶,您基本上没有 访问 UILabel 中的字形信息。
实际上,您无法在 UILabel 中获得那些字形“实际形状”。
尤其是 RobN。指出,一项调查表明,在 UILabel 中,_drawTextInRect:baselineCalculationOnly 完成了工作,那是一大堆临时代码。
情况的总结似乎是 UILabel 简单地早于 NSLayoutManager / Core Text 并且(到目前为止)只是简单地不使用那些现代系统。
只有那些现代系统才能给你这个...
...可以访问 glyph-by-glyph 个形状。
你原来的问题有点毫无疑问,因为它说了两个完全相反的事情。
一方面你说UILabel.
另一方面,您使用 NSLayoutManager 和 NSTextStorage 以及 NSTextContainer 等术语 — TextKit 堆栈。
这些是相反的,因为 UILabel 不是用 TextKit 堆栈绘制的。所以你要求做的就像试图用磁铁吸起一张纸;磁铁确实能举起东西,但它们举起的是磁性材料,纸不是其中之一。可以肯定的是,UILabel 必须以某种确定性的方式进行绘制,但是 什么 这种方式是完全未知且不透明的,并且与 TextKit 无关。
另一方面,UITextView,或者您自己使用 TextKit 绘制的视图,确实使用 TextKit,现在所有 TextKit 工具都适用。因此,如果此 是 一个 UITextView(或您使用 TextKit 自己绘制的视图),那么您要执行的操作会简单得多。
这不需要对界面的外观做太多改变。可以使 UITextView 的行为很像 UILabel(使其不可编辑和不可滚动),但重要的区别是整个文本工具包堆栈是直接公开的。这就是我要开始的地方。我是那些更喜欢 使用 框架而不是 对抗 框架的人之一。