swift text.draw 方法搞乱了 CGContext

标签 swift text core-graphics draw glyph

我正在使用 CoreGraphics 在 CGContext 中绘制单个字形和图元。以下代码适用于 XCode 9.2 中的 swift playground 当在 playground 中启动时,一个带有两倍字母 A 的小矩形应该出现在 playground liveView 的给定坐标处。

import Cocoa
import PlaygroundSupport

class MyView: NSView {

    init(inFrame: CGRect) {
        super.init(frame: inFrame)
    }

    required init?(coder decoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func draw(_ rect: CGRect) {

        // setup context properties

        let context: CGContext = NSGraphicsContext.current!.cgContext

        context.setStrokeColor(CGColor.black)
        context.setTextDrawingMode(.fill)
        context.setFillColor(CGColor(red: 0.99, green: 0.99, blue: 0.85, alpha: 1))
        context.beginPath()
        context.addRect(rect)
        context.fillPath()
        context.setFillColor(.black)

        // prepare variables and constants

        var font = CTFontCreateWithName("Helvetica" as CFString, 48, nil)
        var glyph = CTFontGetGlyphWithName(font, "A" as CFString)
        var glyph1Position = CGPoint(x: self.frame.minX, y: self.frame.maxY/2)
        var glyph2Position = CGPoint(x: self.frame.minX+150, y: self.frame.maxY/2)

        let text = "Hello"
        var textOrigin = NSPoint(x: self.frame.minX+50, y: self.frame.maxY/2)

        // draw one character

        CTFontDrawGlyphs(font, &glyph, &glyph1Position, 1, context)

        // ***   ***   when the next line is uncommented the bug appears   ***   ***

        // text.draw(at: textOrigin)

        CTFontDrawGlyphs(font, &glyph, &glyph2Position, 1, context)
    }
}

var frameRect = CGRect(x: 0, y: 0, width: 200, height: 100)

PlaygroundPage.current.liveView = MyView(inFrame: frameRect)

现在我想在相同的上下文中绘制常规文本。但是,当通过自己的绘制方法在两个字形的绘制之间绘制文本字符串时,当前上下文似乎乱七八糟,第二个字形不会显示。在绘制两个单个字形后绘制文本时,一切都很好。

很明显绘制文本似乎对当前的 CGContext 有影响,但我无法找出到底发生了什么。我在绘制字符串之前尝试了 saveGstate() 方法并在之后恢复,但没有成功。

我还尝试使用 CoreText 方法通过 CTFramesetterCreateWithAttributedString 创建属性字符串并使用 CTFramesetterCreateFrame 显示它,它也不起作用,在创建框架 setter 之后,上下文变得困惑。

我的实际 playground 更复杂,字形并没有完全消失,而是显示在错误的垂直位置,但基本问题 - 问题是相同的:

如何在不在后台对上下文进行任何其他更改的情况下将文本绘制到 currentContext 中?

最佳答案

您需要设置文本矩阵(应用于文本绘制的转换)。您应该始终在绘制文本之前设置它,因为它不是图形状态的一部分,并且可能会被其他绘图代码(例如 AppKit 的字符串绘图扩展)破坏。

在调用 CTFontDrawGlyphs 之前添加以下内容:

    context.textMatrix = .identity

这应该在上下文的初始设置中调用,因为在调用 drawRect 之前没有保证文本矩阵是相同的。然后,任何时候你调用了修改文本矩阵的东西,你都需要将它设置回你想要的东西(在这种情况下是身份,但如果你想以一种奇特的方式绘制它可能是其他东西)。

修改文本矩阵的内容并不总是显而易见的。 AppKit 绘图函数几乎总是如此(尽管我不知道有任何文档表明这一点)。修改上下文的 Core Text 函数,如 CTFrameDrawCTLineDraw,通常会用如下语句记录此事实:

This call can leave the context in any state and does not flush it after the draw operation.

同样 CTFontDrawGlyphs 警告:

This function modifies graphics state including font, text size, and text matrix if these attributes are specified in font. These attributes are not restored.

通常我不鼓励混合文本绘图系统。使用 AppKit 或 Core Text,但不要混用。如果您选择一个并坚持使用,那么这通常不是问题(只要您在 drawRect 的顶部初始化矩阵一次)。例如,如果您使用 CTFontDrawGlyphs 完成所有绘图,则不需要每次都重新设置矩阵;你会留在 Core Text 矩阵中并且它会很好(这就是为什么当你注释掉 draw(at:) 调用时它起作用的原因)。

关于swift text.draw 方法搞乱了 CGContext,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53047093/

相关文章:

java - 将 .txt 文件加载到 textarea Java

ios - 在 MKMapView 上添加具有模糊效果的圆圈

ios - 使用 CGPoint 坐标系绘制矩形 - SWIFT

ios - 观察 UICollectionViewCell 中图层框架大小的变化

php - 如何在 swift 中 http 发布特殊字符

swift - 如何从另一个类更改标签和表格 View ?

ios - Swift 3 URLSession 发送空请求

javascript - 获取选定文本的父元素

文字渐变消失

ios - 如何创建椭圆 CGPathRef?