How to rotate CALayer and cover complete View on device rotation?

60 Views Asked by At

I have applied a gradient to a custom UIView by adding a CAGradientLayer. While this works fine there is small glitch when rotating the device and the view + layer with it. For a short moment the layer does not cover the complete view but the views / background is visible instead. How can this be avoided?

Code:

class BackgroundView: UIView {
    private let gradient : CAGradientLayer = CAGradientLayer()
    
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }
    
    func commonInit() {
        gradient.colors = [UIColor.white.cgColor, UIColor.black.cgColor]
        self.layer.insertSublayer(gradient, at: 0)
    }
    
    
    // Option 1: Adjust layer on sublayer layout
    override func layoutSublayers(of layer: CALayer) {
        super.layoutSublayers(of: layer)
        gradient.frame = self.bounds
    }
    
    // Option 2: Adjust layer on subview layout
    override func layoutSubviews() {
        super.layoutSubviews()
        gradient.frame = self.bounds
    }

    // Option 3: Adjust layer on trait collection change
    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
        super.traitCollectionDidChange(previousTraitCollection)
        gradient.frame = self.bounds
    }
}

The result is exactly the same, no matter if Option1 or Option2 is jused, or both. In all cases the layer is correctly resized during rotation to its new, final size/bounds but not in the same way as the view itself. So the view and its background become visible for a short moment. How to avoid this?

The gradient should cover the whole view at all times.

I found several question on how to rotate a layer with its view. However, all answers provide on of the options I tried. I am doing something wrong or is this glitch normal?

EDIT: Updating the gradient bounds on a trait collection change has the same effect.

1

There are 1 best solutions below

0
DonMag On

You can override the layerClass so the view's "base layer" is a CAGradientLayer instead of adding one as a sublayer.

Take a look at this:

class MyGradientView: UIView {
    
    // this allows us to use the "base" layer as a gradient layer
    //  instead of adding a sublayer
    lazy var gradLayer: CAGradientLayer = self.layer as! CAGradientLayer
    override class var layerClass: AnyClass {
        return CAGradientLayer.self
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() {
        gradLayer.colors = [UIColor.white.cgColor, UIColor.black.cgColor]
        // other initial properties
    }
    
}