Search code examples
iosswiftuiviewautolayoutcalayer

CALayer is not positioned correctly within the View


Context

I am using this simple library to make an intro/walkthrough for my app (TL;DR, horizontal paging view controllers). I currently have 3 pages setup.

In the 3rd walkthrough page, I have a custom CALayer that animates a circle in an endless loop. I'd like to add that layer to a UIView in order to lay it out the way I want in IB (via auto layout).

In viewDidLoad (for the 3rd page) I create the circle layer and set its frame to be the same as the view I positioned, assuming the circle would be in the same spot as the view:

    for v:UIView in [view1!, view2!] {
        var pulse = PulseLayer()
        pulse.frame = v.frame
        pulse.cornerRadius = v.frame.width / 2.0
        pulse.masksToBounds = false
        view.layer.insertSublayer(pulse, above: v.layer)
    }

Problem

When I run the app in the iPhone 6 simulator, the CALayers show up OUTSIDE their UIViews (see below).

CALayers misplaced

One thing I noticed immediately is the layers are not misplaced the same way--one is above its view, and the other is to the left. I am assuming this is related to the constraints on the views, but I cannot figure out how to fix it.

Equally baffling to me is that when run on the iPhone 5 simulator, the layers appear exactly as I expect them to (see below).

Layers correctly placed

I feel like I am misunderstanding some of the concepts at work here. How can I get the positioning to act the same? (Like the iPhone5 gif.)

Or, is there a better way to do what I am trying to do?


Solution

  • viewDidLoad is too soon. Remember, viewDidLoad is way early; the view is not in the interface yet and nothing has its ultimate size/position. If you're going to add the layers to view rather than as sublayers of the little views, you will have to run your layer-creation code much later, in order to get the position right. viewDidAppear: or viewDidLayoutSubviews will be safe - but of course you must use a bool flag so you don't do it too many times.

    Personally I don't see why you don't add the layers to the little views. So you would just set pulse.frame = v.bounds and add as a sublayer to v, not to your view. It solves the positioning and gets the relationships right. And doing it in viewDidLoad would work, because when the views move, the layers move with them.