在图层顶部绘制矩形核心图形?

3

假设你有一个 UIView:

override func draw(_ rect: CGRect) {
    let c = UIGraphicsGetCurrentContext()
    c?.setLineWidth(10.0)
    c?.move(to: CGPoint(x: 10.0, y: 10.0))
    c?.addLine(to: CGPoint(x: 40.0, y: 40.0))
    ... lots of complicated drawing here, sundry paths, colors, widths etc ...
    c?.strokePath()
}

当然,它会自动绘制你的绘图。

但是,在同一个UIView中,如果你这样做...

func setup() { // in inits, layout
    if nil etc {
      thingLayer = CAShapeLayer()
      self.layer.insertSublayer(thingLayer, at: 0)
      thingLayer.fillColor = UIColor.yellow.cgColor }
    thingLayer.frame = bounds
    let path = ... some fancy path
    thingLayer.path = path.cgPath
}

确实,在draw#rect中,新的黄色层是在原图上叠加绘制的。

如何使用核心图形命令在thingLayer上绘制,或者可能在所有图层的顶部另外创建一个图层进行绘制?

核心图形命令:

    let c = UIGraphicsGetCurrentContext()
    c?.setLineWidth(10.0)
    c?.move(to: CGPoint(x: 10.0, y: 10.0))
    c?.addLine(to: CGPoint(x: 40.0, y: 40.0))
    c?.strokePath()

似乎可以直接绘制到主要.layer的上方或上面。

(据我所见,情况似乎是这样。)

您如何使用核心图形命令进行绘制 - 要么在thingLayer上,要么在另一个位于所有图层顶部的图层上?

显然,在draw#rect中,您可以指定要绘制到哪个cgLayer中?

在draw#rect中,您能否创建另一个cgLayer并绘制到上下文中,该cgLayer如何处理?


顺便说一下,如果你基于“bounds”做任何事情,你会想把它移到“layoutSubviews”。 - Rob
1
我不确定你是否注意到了,我会为每个QA添加赏金。 "一个或多个答案是值得额外奖励的典范。" 我发现这通常会带来更多的兴趣和其他有价值的想法、答案或评论。再次感谢! - Fattie
3个回答

4
底线是,draw(_:) 方法用于呈现 UIView 子类的根视图。如果您添加子层,它们将在根视图的 draw(_:) 中完成的任何工作之上呈现。
如果您有一些当前在 draw(_:) 中执行的路径需要在您添加的子层之上呈现,您有几个选项:
  1. 将需要描边的路径移至其自己的CAShapeLayer实例中,并将它们添加到已经添加的其他子层之上。

  2. 将主要的UIView子类视为容器/内容视图,并添加您自己的私有UIView子视图(可能具有“复杂”的draw(_:)方法)。如果不想公开这些私有子视图,可以不必这样做。

  3. 将复杂的绘制移至CALayer子类的draw(in:)中,然后再次将此子层添加到已创建的现有子层之上。

  4. 考虑到thingLayer仅设置背景颜色,您可以只设置视图的背景颜色,而根本不使用该背景颜色的层。


用于渲染UIView子类的根视图。啊啊啊啊啊。(所以,是根cgLayer,对吗?它在根.layer上) - Fattie
从某种意义上说,@Rob的第三项是“问题的答案”。如果您真的想使用draw#rect直接命令,在另一层上,似乎您必须子类化一个层并使用draw#in。用户Alexander在评论中也提到了这一点。 - Fattie
@Fattie 是的,我确实说过那句话。但不知怎么的,我们的评论和内容都被删除了。这是怎么回事? - Alexander

1

CALayer层级结构是一棵树,根层(例如窗口背景所包含的层)在前,其后是它的sublayersCALayer对象数组,按从后向前的顺序存储和绘制),然后是它们的子层,以此类推。

您实现的UIView.draw(_ rect: CGRect)定义了视图的主层(self.layer)的绘制方式。

通过将CAShapeLayer作为self.layer的子层添加,您将使它在self.layer之后被绘制,从而使它被绘制在self.layer之上,后者包含您在UIView.draw(_ rect: CGRect)中绘制的线条。

要解决这个问题,您需要将描边放在thingLayer之后的子层中。

self.layer.insertSublayer(thingLayer, at: 0)
self.layer.insertSublayer(lineLayer, above: thingLayer)

顺便提一下,你忘记委托给super.draw(rect)了。对于直接继承自UIView的子类来说这并不是必需的(因为基本实现没有做任何事情),但对于其他所有情况都是必要的,并且养成这个习惯通常是一个好习惯(以防止您遇到真正晦涩/令人沮丧的绘图错误)。

0

如果您的黄色形状图层不能独立更改或移动,您可以摆脱CAShapeLayer并在draw(_:)的顶部自己绘制形状:

let path = // your fancy UIBezierPath
UIColor.yellow.setFill()
path.fill()

否则,正如其他评论者所说,您需要将绘图代码移到额外的自定义UIView或CALayer子类中,并按照您想要的顺序堆叠它们。

Robin,确实如此:但总的来说,在计算机中将东西分开是很好的。只举一个例子,我们可能会以某种方式对一些图层进行动画处理;在这种情况下,拥有各种图层真的很明智/更容易。 - Fattie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接