2

I am trying to set the background on my UIView to a gradient. I have the following in my UIViewController's viewDidLoad method:

let gradientLayer = CAGradientLayer()
gradientLayer.colors = [UIColor.blue70.cgColor, UIColor.blue60.cgColor, UIColor.blue70.cgColor]
gradientLayer.startPoint = CGPoint(x: 0, y: 0)
gradientLayer.endPoint = CGPoint(x: 1, y: 1 )
gradientLayer.frame = self.view.bounds

self.view.layer.insertSublayer(gradientLayer, at: 0)

The gradient renders as expected. However, when I rotate the device the gradient does not redraw and is no longer rendered properly. Rotating from portrait->landscape leaves me with a blank section on the right or left. Rotating from landscape->portrait leaves me with a blank section at the bottom. I tried moving this code to viewDidLayoutSubviews, but that didn't solve it. Any recommendations on how to accomplish this?

Shadowman
  • 11,150
  • 19
  • 100
  • 198
  • Your main issue is that you are adding multiple gradient layers at the bottom of your view but only the one at the top will be visible. What you need is to make sure you add just one gradient layer and adjust its frame in viewDidLayoutSubviews method `gradientLayer.frame = view.bounds` – Leo Dabus Apr 07 '19 at 01:23

3 Answers3

3

What i would recommend here is to add the gradient in the viewWillLayoutSubviews() method

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()

    self.view.addGradiant()
}

But now we would have to remove the old layer When adding the new one with the new frames(In case the Phone rotates)

We can then first remove the layer and add the new one like in this extension method

extension UIView {

func addGradiant() {
    let GradientLayerName = "gradientLayer"

    if let oldlayer = self.layer.sublayers?.filter({$0.name == GradientLayerName}).first {
        oldlayer.removeFromSuperlayer()
    }

    let gradientLayer = CAGradientLayer()
    gradientLayer.colors = [UIColor.blue.cgColor, UIColor.red.cgColor, UIColor.green.cgColor]
    gradientLayer.startPoint = CGPoint(x: 0, y: 0)
    gradientLayer.endPoint = CGPoint(x: 1, y: 1 )
    gradientLayer.frame = self.bounds
    gradientLayer.name = GradientLayerName

    self.layer.insertSublayer(gradientLayer, at: 0)
}

}
Razi Tiwana
  • 1,425
  • 2
  • 13
  • 16
0

Make gradientLayer view controller's property.

let gradientLayer = CAGradientLayer()

Add gradientLayer in the viewDidLoad in the same way you did.

Set the frame in viewWillLayoutSubviews

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()
    gradientLayer.frame = self.view.bounds
}
HMHero
  • 2,333
  • 19
  • 11
0

While you can do as the other answers suggest and update your gradient layer bounds in viewDidLayoutSubviews, the more reusable way to do things is to make a gradient view subclass who's layerClass is a CAGradientLayer. Such a view will have its layer automatically resized to fit the view (because the gradient layer is now the backing layer for the view and not some subLayer) and you can use this view anywhere just by changing the class of your view in your nib file or storyboard:

class GradientView: UIView {
    var colors: [UIColor] = [.red, .white] {
        didSet {
            setup()
        }
    }
    var locations: [CGFloat] = [0,1] {
        didSet {
            setup()
        }
    }
    var startPoint: CGPoint = .zero  {
        didSet {
            setup()
        }
    }
    var endPoint: CGPoint = CGPoint(x: 1, y: 1) {
        didSet {
            setup()
        }
    }

    override class var layerClass : AnyClass { return CAGradientLayer.self }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }
    override func awakeFromNib() {
        super.awakeFromNib()
        setup()
    }
    private func setup() {
        guard let layer = self.layer as? CAGradientLayer else {
            return
        }
        layer.colors = colors.map { $0.cgColor }
        layer.locations = locations.map { NSNumber(value: Double($0)) }
        layer.startPoint = startPoint
        layer.endPoint = endPoint
    }
}
Josh Homann
  • 15,933
  • 3
  • 30
  • 33