Kodeco Forums

CALayer Tutorial for iOS: Getting Started

In this tutorial, you'll learn what a CALayer is, and see 10 examples of using CALayers for cool effects, like shapes, gradients, and even particle systems.


This is a companion discussion topic for the original entry at https://www.raywenderlich.com/402-calayer-tutorial-for-ios-getting-started

Thank you for very practical sharing!:smiley:

1 Like

Thank you for this. FYI. In going through the CATileLayer tutorial I got a backtrace:

LayerPlayer[1522:262128] [reports] Main Thread Checker: UI API called on a background thread: -[UIView bounds]

It appears in TilingView.swift, you are accessing bounds from draw(). XCode 9 reports “UIView.bounds must be used from main thread only” on line 59.

Hi,

I have the same issue. the problem is that it crash there randomly, when you are using the app compiled for production.
so it is more bad than a simple warning…

Thank you @ormaa and @engyew for giving us a heads up! Will solve it as soon as possible :+1:

Would anyone know if CATextLayer inherit the transform of the parent layer? I am trying it out, and have set the root layer transform to be flipped, CGAffineTransform(a: 1.0, b: 0, c: 0, d: -1.0, tx: 0, ty: 0).

But when I add a CATextLayer to it, layer.addSublayer(labelLayer), it is rendered incorrectly, ignoring the transform that is set for the parent. The other layers, CAShapeLayers, do inherit the transform and therefore are rendered correctly.

Am I missing something?

@michalciurus Can you please help with this when you get a chance? Thank you - much appreciated! :]

@insha

CATextLayer should inherit the transform from the parent layer.
Here’s a snippet I used to do just that - it did apply transform to CATextLayer sublayer.

let textLayer = CATextLayer()
textLayer.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
    
let string = String(repeating: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce auctor arcu quis velit" ,count: 20)
    
textLayer.string = string
    
textLayer.font = CTFontCreateWithName("Helvetica" as CFString, 4, nil)
    
textLayer.foregroundColor = UIColor.darkGray.cgColor
textLayer.isWrapped = true
textLayer.alignmentMode = kCAAlignmentLeft
textLayer.contentsScale = UIScreen.main.scale
    
let transform = CGAffineTransform(a: 1.0, b: 0, c: 0, d: -1.0, tx: 0, ty: 0)
self.layer.setAffineTransform(transform)
self.layer.addSublayer(textLayer)

Let me know if it helps! Please share your code, maybe I can be of assistance.

Hi @michalciurus,

My excerpted code is below (hope it makes sense; otherwise let me know):

final class Graph: UIView
{
    private var flippedTransform  = CGAffineTransform(a: 1.0, b: 0, c: 0, d: -1.0, tx: 0, ty: 0)

    ...

    override func draw(_ rect: CGRect)
    {
        layer.setAffineTransform(flippedTransform)

        drawDataPoints(points: points,
                       animation: configuration.dataPointAnimation,
                       pointShape: configuration.pointShape)
    }

    private func drawDataPoints(points: [CGPoint])
    {
        for point in points
        {
            let circlePath         = UIBezierPath()
            let radius   : CGFloat = 2.0
            let lineWidth: CGFloat = 1.0
            let color              = UIColor.blue

            circlePath.addArc(withCenter: point,
                              radius: radius,
                              startAngle: 0,
                              endAngle: CGFloat(Double.pi * 2),
                              clockwise: true)

            let circleShapeLayer = CAShapeLayer()

            circleShapeLayer.fillColor   = color.cgColor
            circleShapeLayer.lineWidth   = lineWidth
            circleShapeLayer.strokeColor = color.cgColor
            circleShapeLayer.frame       = bounds
            circleShapeLayer.path        = circlePath.cgPath
            circleShapeLayer.opacity     = 1.0

            layer.addSublayer(circleShapeLayer)

            let textLayer = CATextLayer()

            textLayer.string          = "Value Label"
            textLayer.alignmentMode   = kCAAlignmentCenter
            textLayer.font            = configuration.fonts.valueLabel
            textLayer.fontSize        = configuration.fonts.valueLabel.pointSize
            textLayer.foregroundColor = color.cgColor
            textLayer.frame           = CGRect(x: point.x, y: point.y, width: 100, height: 20)

            layer.addSublayer(textLayer)
        }
    }
}

configuration in the above is just a struct with a fonts property.

This results in the points being plotted correctly based on the flipped coordinates, but the text layer is drawn incorrectly (see below).
26 PM

Thank you for your reply and offer of assistance; I really appreciate your time and effort.

Everything is drawn correctly according to the view hierarchy you’ve set up :slight_smile:

There are two problems:

  1. The width of the label is too big it seems, the text is centered so the text seems to be far away from the point it should describe. Try to decrease width from 100 to (smaller value) or use kCAAlignmentLeft.
  2. The CATextLayer gets flipped as expected, but that means it’s unreadable. You have to flip it back locally to make it readable.

The second one is tricky. You basically hit a math problem. Please read this and use a piece of paper and try to draw everything out.

You need to put CATextLayer in a wrapper view:

wrapperView.frame = CGRect(x: point.x, y: point.y, width: 100, height: 20)
textLayer.frame = CGRect(x: 0, y: 0, width: wrapperView.width, height: wrapperView.height)

wrapperView.addSublayer(textLayer)
layer.addSublayer(wrapperView)

textLayer.setAffineTransform(flippedTransform) //Flip back the text but without moving it in space

If what you are saying is correct, does that mean that the CATextLayer ignores the parent layer transform when drawing the text, not necessarily the layer, but the text string that is drawn in it?

Also, I picked 100 as width somewhat arbitrarily, the behavior is the same with a width of 20. The width and alignment are not the main problem I am trying to solve.

CATextLayer should inherit the transform from the parent layer.

You’re right, my mistake. That’s actually incorrect, sorry. What I meant and what is happening is that the CATextLayer is affected by the transform, but does not “inherit” the value. CATextLayer transform is identity, it does not inherit the value from the parent. It’s true for every layer. In fact that’s the standard in any 2d/3d drawing framework - children do not “inherit” transforms, their transforms are identity by default.

Imagine the transforms being applied from back to front. That means that everything is drawn normally including text and in the end the whole view gets flipped.

This tutorial is more than six months old so questions are no longer supported at the moment for it. Thank you!