swiftuiviewcagradientlayer

How to add sublayer such as CAGradientLayer on view.bounds?


How can I add my gradient layer as a sub-layer to my view with the same size, which I want to use in other classes?

I tried to do it with "self.bounds" but it didn't work. If I give it strict dimensions, for example: gradientLayer.frame = CGRect(x: 0, y: 0, width: 100, height: 100) - it will work, but I need the same size as my view.

final class ShimmerLabel: UIView {
    
    private lazy var backShimmerTextLabel: UILabel = {
        let label = UILabel()
        label.text = "Shimmer"
        label.textColor = UIColor(red: 65/255, green: 65/255, blue: 65/255, alpha: 1)
        label.font = UIFont.systemFont(ofSize: 50)
        label.textAlignment = .center
        return label
    }()
    
    private lazy var frontShimmerTextLabel: UILabel = {
        let label = UILabel()
        label.text = "Shimmer"
        label.textColor = .white
        label.font = UIFont.systemFont(ofSize: 50)
        label.textAlignment = .center
        return label
    }()
    
    init() {
        super.init(frame: .zero)
        configureGradientLayer()
        configureLayout()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func configureGradientLayer() {
        
        let gradientLayer = CAGradientLayer()
        gradientLayer.colors = [UIColor.white.cgColor, UIColor.black.cgColor]
        gradientLayer.locations = [0, 1]
        
        gradientLayer.frame = bounds // Dont work with bounds :((
        
        layer.addSublayer(gradientLayer)
    }
    
    private func configureLayout() {
        
        addSubview(backShimmerTextLabel)
        backShimmerTextLabel.snp.makeConstraints { make in
            make.width.height.equalToSuperview()
        }

        addSubview(frontShimmerTextLabel)
        frontShimmerTextLabel.snp.makeConstraints { make in
            make.width.height.equalToSuperview()
        }
    }
}

Solution

  • You can use the "base" layer of a view as a CAGradientLayer like this:

    // use the "base" layer as a gradient layer
    lazy var gradientLayer: CAGradientLayer = self.layer as! CAGradientLayer
    override class var layerClass: AnyClass {
        return CAGradientLayer.self
    }
    

    So your class becomes:

    final class ShimmerLabel: UIView {
        
        private lazy var backShimmerTextLabel: UILabel = {
            let label = UILabel()
            label.text = "Shimmer"
            label.textColor = UIColor(red: 65/255, green: 65/255, blue: 65/255, alpha: 1)
            label.font = UIFont.systemFont(ofSize: 50)
            label.textAlignment = .center
            return label
        }()
        
        private lazy var frontShimmerTextLabel: UILabel = {
            let label = UILabel()
            label.text = "Shimmer"
            label.textColor = .white
            label.font = UIFont.systemFont(ofSize: 50)
            label.textAlignment = .center
            return label
        }()
        
        // use the "base" layer as a gradient layer
        lazy var gradientLayer: CAGradientLayer = self.layer as! CAGradientLayer
        override class var layerClass: AnyClass {
            return CAGradientLayer.self
        }
    
        init() {
            super.init(frame: .zero)
            configureGradientLayer()
            configureLayout()
        }
        
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        
        private func configureGradientLayer() {
            gradientLayer.colors = [UIColor.white.cgColor, UIColor.black.cgColor]
            gradientLayer.locations = [0, 1]
        }
        
        private func configureLayout() {
            
            addSubview(backShimmerTextLabel)
            backShimmerTextLabel.snp.makeConstraints { make in
                make.width.height.equalToSuperview()
            }
            
            addSubview(frontShimmerTextLabel)
            frontShimmerTextLabel.snp.makeConstraints { make in
                make.width.height.equalToSuperview()
            }
        }
    }
    

    and the gradient will always fill the view frame. No need to add sublayer(s).


    Edit

    If you need the gradient layer to be a sublayer, you need to update its frame in layoutSubviews():

    override func layoutSubviews() {
        super.layoutSubviews()
        gradientLayer.frame = bounds
    }
    

    So, make let gradientLayer = CAGradientLayer() a property of your view...

    final class ShimmerLabel: UIView {
        
        private let gradientLayer: CAGradientLayer = CAGradientLayer()
    
        private lazy var backShimmerTextLabel: UILabel = {
            let label = UILabel()
            label.text = "Shimmer"
            label.textColor = UIColor(red: 65/255, green: 65/255, blue: 65/255, alpha: 1)
            label.font = UIFont.systemFont(ofSize: 50)
            label.textAlignment = .center
            return label
        }()
        
        private lazy var frontShimmerTextLabel: UILabel = {
            let label = UILabel()
            label.text = "Shimmer"
            label.textColor = .white
            label.font = UIFont.systemFont(ofSize: 50)
            label.textAlignment = .center
            return label
        }()
        
        init() {
            super.init(frame: .zero)
            configureGradientLayer()
            configureLayout()
        }
        
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        
        private func configureGradientLayer() {
            gradientLayer.colors = [UIColor.white.cgColor, UIColor.black.cgColor]
            gradientLayer.locations = [0, 1]
            layer.addSublayer(gradientLayer)
        }
        
        private func configureLayout() {
            
            addSubview(backShimmerTextLabel)
            backShimmerTextLabel.snp.makeConstraints { make in
                make.width.height.equalToSuperview()
            }
            
            addSubview(frontShimmerTextLabel)
            frontShimmerTextLabel.snp.makeConstraints { make in
                make.width.height.equalToSuperview()
            }
        }
        
        override func layoutSubviews() {
            super.layoutSubviews()
            // update gradient layer frame
            gradientLayer.frame = bounds
        }
    }