iosswiftautolayoutcalayer

layerClass breaks auto layout


I made a custom view which has CAGradientLayer as a layer, and I added this view on the other view with bottom anchor. It was just very simple auto layout constraint with bottom, leading, trailing and height constraints. However the bottom constraint doesn't work and it is always at the top(y == 0).

class GradientView: UIView {
    override class var layerClass: AnyClass { CAGradientLayer.self }
    private var gradientLayer: CAGradientLayer { layer as! CAGradientLayer }

    var colors: [UIColor] = [.clear, .white] { didSet { setNeedsLayout() } }
    var locations: [NSNumber] = [0, 1] { didSet { setNeedsLayout() } }

    override func layoutSubviews() {
        super.layoutSubviews()

        gradientLayer.frame = bounds
        gradientLayer.colors = colors.map { $0.cgColor }
        gradientLayer.locations = locations
    }
}
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        let gradient = GradientView()
        gradient.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(gradient)
        gradient.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
        gradient.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        gradient.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
        gradient.heightAnchor.constraint(equalToConstant: 100).isActive = true
    }
}

The result is like below. The gradient view is at the top, not bottom of the view.

enter image description here

I tried almost everything I could imagine and finally removed the layerClass and added the CAGradientLayer as a sublayer, and the constraint started working.

Is there anything I missed to override layerClass or it is kind of a bug?


Solution

  • You are the one breaking autolayout. Delete this line:

    gradientLayer.frame = bounds
    

    The gradient, and the GradientView, will then appear where you expect them.