Search code examples
iosswiftcore-animationcalayer

CALayer disappears when storing in a property


I have a custom UIPresentationController in which I overrode the presentedView as follows:

override var presentedView: UIView? {
        guard let view = super.presentedView else { return nil }
        
        let shadowLayer = CALayer()
        shadowLayer.shadowPath = .init(rect: view.bounds, transform: .none)
        shadowLayer.shadowRadius = 6
        shadowLayer.shadowColor = .init(gray: 0, alpha: 0.15)
        shadowLayer.backgroundColor = view.layer.backgroundColor
        shadowLayer.shadowOpacity = 1
        shadowLayer.shadowOffset = .init(width: 0, height: -10)
        shadowLayer.shouldRasterize = true
        shadowLayer.rasterizationScale = UIApplication.screen?.scale ?? 1.0
        
        view.layer.insertSublayer(shadowLayer, at: 0)
        shadowLayer.position = view.layer.position
        shadowLayer.bounds = view.bounds
        view.layer.masksToBounds = false
        
        return view
    }

Then for efficiency I thought why creating a new CALayer every time presetedView is calculated and decided to hold the the layer as a property of the class and only update the shadowPath. After that the layer is no longer visible and not in the view hierarchy anymore. I also tried to insert sublayer only once by checking if layer is present in the sublayers but it didn't help

Does anyone know what am I missing?

UPDATE: Declared a property

private let shadowLayer = {
        let shadowLayer = CALayer()
        shadowLayer.shadowRadius = 6
        shadowLayer.shadowOpacity = 1
        shadowLayer.shadowOffset = .init(width: 0, height: -10)
        shadowLayer.shouldRasterize = true
        shadowLayer.rasterizationScale = UIApplication.screen?.scale ?? 1.0
        shadowLayer.masksToBounds = false
        return shadowLayer
    }()

And deleted initialization code from here. After that the layer dissappears

    override var presentedView: UIView? {
        guard let view = super.presentedView else { return nil }
        

        shadowLayer.shadowPath = .init(rect: view.bounds, transform: .none)

        shadowLayer.shadowColor = .init(gray: 0, alpha: 0.15)
        shadowLayer.backgroundColor = view.layer.backgroundColor

        
        view.layer.insertSublayer(shadowLayer, at: 0)
        shadowLayer.position = view.layer.position
        shadowLayer.bounds = view.bounds
        view.layer.masksToBounds = false
        
        return view
    }

Solution

  • Turns out I was calculating layers position & shadowPath wrong! Because sublayers coordinating system is relative to superlayers system we should draw path inside views bounds not frame and calculate center point of the frame manually

        override var presentedView: UIView? {
            guard let view = super.presentedView else { return nil }
            
            
            shadowLayer.shadowPath = .init(rect: view.bounds, transform: .none)
            shadowLayer.position = .init(x: view.bounds.midX, y: view.bounds.midY)
            shadowLayer.bounds = view.bounds
            shadowLayer.backgroundColor = view.layer.backgroundColor
    
            
            if shadowLayer.superlayer == nil {
                view.layer.insertSublayer(shadowLayer, below: nil)
            }
            
            view.layer.masksToBounds = false
            
            return view
        }