Search code examples
iosswiftcore-animationcalayer

Why would a property to reference a CALayer's parent view be 'nil' during animation


I have been trying to keep a reference to a subclassed CALayer's parent view, but it becomes nil during animation and I would like to know why.

While containerWidth is animated, parentView is nil.

198.0648398399353
self.delegate: Optional(<Project.ContainerView: 0x7fc037a05370 ...
parentView: nil

Before and after the animation, parentView is not nil.

200.0
self.delegate: Optional(<Project.ContainerView: 0x7fc037a05370 ...
parentView: Optional(<Project.ContainerView: 0x7fc037a05370 ...
class ContainerLayer: CALayer {
    var didSetup = false
    var parentView: ContainerView!
    @NSManaged var containerWidth: CGFloat

    override func layoutSublayers() {
        super.layoutSublayers()
        if !self.didSetup {
            parentView = self.delegate as? ContainerView
            self.didSetup = true
        }
    }
    override class func needsDisplay(forKey key: String) -> Bool {
        if key == #keyPath(containerWidth) {
            return true
        }
        return super.needsDisplay(forKey: key)
    }

    override func draw(in ctx: CGContext) {
        print(containerWidth)
        print("self.delegate: \(self.delegate)")
        print("parentView: \(parentView)")
    }
}

Solution

  • During the animation, your layer is copied, and the whole animation is shown on the copied instance of the layer.

    In this way CoreAnimation differs between two states - model state and presentation state. The layer which you work with, carries model state, and animation is rendered on presentationLayer.

    Presentation layer is created via init(layer: Any) initialiser, where layer argument is the model layer.

    You can assign the required properties in this initialiser from the model layer to the presentation layer, like this:

    class ContainerLayer: CALayer {
    
        private override init(layer: Any) {
           super.init(layer: layer)
           if let containerModelLayer = layer as? ContainerLayer {
               self.parentView = containerModelLayer.parentView
           }
        }
        
        ...
    }