在 UILabel 的 draw#rect 中获取字形 boundingRect
Posted
技术标签:
【中文标题】在 UILabel 的 draw#rect 中获取字形 boundingRect【英文标题】:Getting a glyph boundingRect in draw#rect in UILabel 【发布时间】:2019-07-08 13:23:05 【问题描述】:使用 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 的品质,例如“居中文本”? (不过,我相信您实际上不能直接访问 UILabel 的 layoutManager 吗?)
我们应该使用类似 CTLineGetOffsetForStringIndex 的东西吗?这甚至可能在draw#rect中
请注意,除了没有正确的偏移量外,绿色框似乎还是错误的高度。 (它看起来更像是一个普通的旧的 intrinsicContentSize 而不是字形边界框。)
怎么做?
为了记录,我的总体目标是根据实际的字形框(当然对于“y”、“X”等不同)来移动字形。但总的来说,了解字形的框有很多有用的理由。
【问题讨论】:
很简单,TextKit 堆栈与 UILabel 没有直接关系的问题不是吗? UILabel 绘图不是 TextKit 绘图。如果这是一个 UITextView,或者只是一个您使用 TextKit 自己绘制的视图,那么您要求做的事情会更加直接。 是的,但是您将很难找出偏移量是多少。 “你会认为有办法” 不,我不会。也许你会这么想。就个人而言,如果我有一个像拖动字形这样的目标,我不会从 UILabel 开始。我是那些喜欢使用框架而不是对抗它的人之一。 基本上,是的。可以使 UITextView 的行为与 UILabel 非常相似(使其不可编辑和不可滚动),但重要的区别是整个文本工具包堆栈直接公开。这就是我要开始的地方。当然,你做什么取决于你。 "他们不会在 UILabel 中公开 layoutManager。"为什么你认为 UILabel 有一个布局管理器?我没有看到有人使用 lldb 或 Hopper。我可能会遗漏一些东西,但我不会特别期望 Apple 会改造 UILabel 以使用 NSLayoutManager。这是可能的,但你有理由期待这是真的吗?反编译drawText(in:)
,好像是调用textRect(forBounds:)
,好像是手工布局。我没有看到任何布局管理器。
过去我已经构建了您所描述的几次;我总是用 Core Text 完成布局。如果我今天再写一次,也许我会使用 TextKit。但我不明白你为什么要在 UILabel 上构建它。如果只是出于好奇,那就太好了,但我会花一些时间在 Hopper 中看看 UILabel 是如何在幕后工作的。它主要基于 CoreGraphics 和 NSAttributedString,而不是 CoreText 或 NSLayoutManager。
【参考方案1】:
你最初的问题是一个没有问题的问题,因为它说了两个完全相反的东西。
一方面,你说 UILabel。
另一方面,您使用诸如 NSLayoutManager 和 NSTextStorage 和 NSTextContainer 之类的术语——TextKit 堆栈。
这些是相反的,因为 UILabel 不是使用 TextKit 堆栈绘制的。所以你要求做的就像试图用磁铁举起一张纸;磁铁确实能提升东西,但它们提升的是磁性材料,而纸不是其中之一。可以肯定的是,UILabel 必须以某种确定性的方式进行绘制,但 这种方式是完全未知且不透明的,与 TextKit 无关。
另一方面,UITextView 或您自己使用 TextKit 绘制的视图,确实使用了 TextKit,现在所有的 TextKit 工具都适用。因此,如果这个 是 UITextView(或您使用 TextKit 自己绘制的视图),您要求做的事情会更加直接。
这不需要对界面的外观有太大的改变。可以使 UITextView 的行为与 UILabel 非常相似(使其不可编辑和不可滚动),但重要的区别是整个文本工具包堆栈直接公开。这就是我要开始的地方。我是那些喜欢使用框架而不是对抗它的人之一。
【讨论】:
【参考方案2】:原来这个问题的答案似乎是:
事实上,无论是否令人惊讶,您基本上没有访问 UILabel 中的字形信息。
实际上,您无法在 UILabel 中获得那些字形“实际形状”。
特别是 RobN。曾指出,一项调查表明,在 UILabel 中,_drawTextInRect:baselineCalculationOnly 完成了这项工作,这是一大堆临时代码。
情况的总结似乎是 UILabel 只是早于 NSLayoutManager / Core Text 并且(到目前为止)只是简单地不使用那些现代系统。
只有那些现代系统才能给你这个......
... 一种逐个字形的访问方式。
【讨论】:
以上是关于在 UILabel 的 draw#rect 中获取字形 boundingRect的主要内容,如果未能解决你的问题,请参考以下文章
NSString draw(in: rect) 方法自己改变 rect
@IBDesignable UiView draw(_ rect: CGRect) 在 IB 中有效,但在设备上无效
我的 TGraphic.Draw(Canvas, Rect) 代码不起作用
setNeedsDisplay() 没有调用 draw(_ rect: CGRect)
draw(_ rect: CGRect) 不绘制超过一定尺寸
如何从 cellForRowAt 调用 draw(_ rect: CGRect) 在自定义 tableview 上绘图?