首页 > 解决方案 > 使用 Swift 在 NSView 中使用图层时如何绘制文本

问题描述

我在我的 NSView 中使用图层,因为大部分显示不会经常更改。这工作正常。我现在正要向图层添加文本。这是行不通的。

要绘制文本,我正在尝试使用另一个答案中的代码来了解如何在曲线上绘制文本。我已经研究过,但我尝试过的没有任何帮助。我创建了一个 Playground 来演示。当我运行 Playground 时,我可以看到实际调用了显示文本的代码,但屏幕上没有呈现任何内容。我确定我有什么问题,只是不确定是什么。

下面是操场,以及上面引用的代码(针对 Swift 5、NS* 与 UI* 进行了更新,并删除了大小的注释)。

import AppKit
import PlaygroundSupport

func centreArcPerpendicular(text str: String,
                            context: CGContext,
                            radius r: CGFloat,
                            angle theta: CGFloat,
                            colour c: NSColor,
                            font: NSFont,
                            clockwise: Bool){

   let characters: [String]         = str.map { String($0) }
   let l                            = characters.count
   let attributes                   = [NSAttributedString.Key.font: font]

   var arcs: [CGFloat] = []
   var totalArc: CGFloat = 0

   for i in 0 ..< l {
      arcs                          += [chordToArc(characters[i].size(withAttributes: attributes).width, radius: r)]
      totalArc                      += arcs[i]
   }

   let direction: CGFloat           = clockwise ? -1 : 1
   let slantCorrection: CGFloat     = clockwise ? -.pi / 2 : .pi / 2

   var thetaI                       = theta - direction * totalArc / 2

   for i in 0 ..< l {
      thetaI                        += direction * arcs[i] / 2
      centre(text: characters[i],
             context: context,
             radius: r,
             angle: thetaI,
             colour: c,
             font: font,
             slantAngle: thetaI + slantCorrection)
      thetaI                        += direction * arcs[i] / 2
   }
}

func chordToArc(_ chord: CGFloat, radius: CGFloat) -> CGFloat {
   return 2 * asin(chord / (2 * radius))
}

func centre(text str: String,
            context: CGContext,
            radius r: CGFloat,
            angle theta: CGFloat,
            colour c: NSColor,
            font: NSFont,
            slantAngle: CGFloat) {
   let attributes                   = [NSAttributedString.Key.foregroundColor: c, NSAttributedString.Key.font: font]
   context.saveGState()
   context.translateBy(x: r * cos(theta), y: -(r * sin(theta)))
   context.rotate(by: -slantAngle)
   let offset                       = str.size(withAttributes: attributes)
   context.translateBy (x: -offset.width / 2, y: -offset.height / 2)
   str.draw(at: CGPoint(x: 0, y: 0), withAttributes: attributes)
   context.restoreGState()
}
class CustomView: NSView {
   let textOnCurve                 = TextOnCurve()

   override init(frame: NSRect) {
      super.init(frame: frame)
      wantsLayer                   = true
   }
   required init?(coder aDecoder: NSCoder) {
      fatalError("init(coder:) has not been implemented")
   }
   override func updateLayer() {
      let textOnCurveLayer          = CAShapeLayer()
      textOnCurveLayer.frame        = self.frame
      textOnCurveLayer.delegate     = textOnCurve
      textOnCurveLayer.setNeedsDisplay()
      layer?.addSublayer(textOnCurveLayer)
   }
}

class TextOnCurve: NSObject, CALayerDelegate {
   func draw(_ layer: CALayer, in ctx: CGContext) {
      ctx.setFillColor(.white)
      ctx.move(to: CGPoint(x: 100, y: 100))
      ctx.addLine(to: CGPoint(x: 150, y: 100))
      ctx.addLine(to: CGPoint(x: 150, y: 150))
      ctx.addLine(to: CGPoint(x: 100, y: 150))
      ctx.closePath()
      ctx.drawPath(using: .fill)

      ctx.translateBy(x: 200.0, y: 200.0)
      centreArcPerpendicular(text: "Anticlockwise",
                             context: ctx,
                             radius: 100,
                             angle: CGFloat(-.pi/2.0),
                             colour: .red,
                             font: NSFont.systemFont(ofSize: 16),
                             clockwise: false)
      centre(text: "Hello flat world",
             context: ctx,
             radius: 0,
             angle: 0 ,
             colour: .yellow,
             font: NSFont.systemFont(ofSize: 16),
             slantAngle: .pi / 4)
   }
}


let containerView = CustomView(frame: CGRect(x: 0, y: 0, width: 400, height: 400))
PlaygroundPage.current.liveView = containerView

框已绘制,但文本未绘制。

如果我不使用图层,那么我可以通过调用标准draw:方法中的方法来绘制文本。所以代码应该可以工作,但是,关于层的使用会阻止它。

标签: swiftcalayerdesktop

解决方案


推荐阅读