Denys  Triasunov
Denys Triasunov

Reputation: 569

Changing view gradient background in real time

I am practicing with gradient backgrounds for a bigger app and created this dummy app to simplify the concept. The view has a 50/50 gradient background and 2 rows of colored buttons.

I want the user to be able to play with colors of the gradient by tapping upper and lower buttons, so that gradient background changes accordingly to buttons color.

enter image description here

ViewController's code is following:

    import UIKit

    class ViewController: UIViewController {

        var currentUpperColor = UIColor.black
        var currentLowerColor = UIColor.white

        override func viewDidLoad() {
            super.viewDidLoad()

            setGradientBackground(lowerColor: currentLowerColor, upperColor: currentUpperColor)
        }

        func setGradientBackground(lowerColor: UIColor, upperColor: UIColor) {

            let gradientLayer = CAGradientLayer()
            gradientLayer.frame = view.bounds
            gradientLayer.colors = [lowerColor.cgColor, upperColor.cgColor]
            gradientLayer.locations = [0.5, 1.0]
            gradientLayer.startPoint = CGPoint(x: 0.0, y: 1.0)
            gradientLayer.endPoint = CGPoint(x: 0.0, y: 0.0)

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

        }

        @IBAction func upperColorPicked(_ sender: UIButton) {
            currentUpperColor = sender.backgroundColor!
            setGradientBackground(lowerColor: currentLowerColor, upperColor: currentUpperColor)
        }

        @IBAction func lowerColorPicked(_ sender: UIButton) {
            currentLowerColor = sender.backgroundColor!
            setGradientBackground(lowerColor: currentLowerColor, upperColor: currentUpperColor)
        }

        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }


}

But when the app runs, nothing happens when buttons are tapped. I understand some kind of view redraw should occur, but I'm not sure how to call it.

Upvotes: 2

Views: 1240

Answers (3)

Swathi
Swathi

Reputation: 115

The code shows that you have declared the CAGradientLayer() as a local variable. So it means that every time you want to update the button a gradientLayer gets inserted/ added to the view. The layer must be added only once and the colors must be updated according to the button actions.

var currentUpperColor = UIColor.black
var currentLowerColor = UIColor.white
let gradientLayer = CAGradientLayer()

override func viewDidLoad() {
    super.viewDidLoad()
    setGradientBackground(lowerColor: currentLowerColor, upperColor: currentUpperColor)
}
func setGradientBackground(lowerColor: UIColor, upperColor: UIColor) {
    gradientLayer.frame = view.bounds
    gradientLayer.colors = [lowerColor.cgColor, upperColor.cgColor]
    gradientLayer.locations = [0.5, 1.0]
    gradientLayer.startPoint = CGPoint(x: 0.0, y: 1.0)
    gradientLayer.endPoint = CGPoint(x: 0.0, y: 0.0)
    view.layer.insertSublayer(gradientLayer, at: 0)
}
@IBAction func upperColorPicked(_ sender: UIButton) {
    currentUpperColor = sender.backgroundColor!
    gradientLayer.colors = [currentLowerColor.cgColor, currentUpperColor.cgColor]
}
@IBAction func lowerColorPicked(_ sender: UIButton) {
    currentLowerColor = sender.backgroundColor!
    gradientLayer.colors = [currentLowerColor.cgColor, currentUpperColor.cgColor]
}

Upvotes: 0

Krunal
Krunal

Reputation: 79646

I tried your code and here it seems found working with following changes:

import UIKit

class GradientController: UIViewController {

    @IBOutlet weak var vwButtonContainer: UIView!

    var currentUpperColor = UIColor.black
    var currentLowerColor = UIColor.white
    var gradientLayer = CAGradientLayer()

    override func viewDidLoad() {
        super.viewDidLoad()

        setGradientBackground(lowerColor: currentLowerColor, upperColor: currentUpperColor)
    }

    func setGradientBackground(lowerColor: UIColor, upperColor: UIColor) {

        if gradientLayer != nil {
           gradientLayer.removeFromSuperlayer()
        }
        gradientLayer = CAGradientLayer()
        gradientLayer.frame = view.bounds
        gradientLayer.colors = [lowerColor.cgColor, upperColor.cgColor]
        gradientLayer.locations = [0.5, 1.0]
        gradientLayer.startPoint = CGPoint(x: 0.0, y: 1.0)
        gradientLayer.endPoint = CGPoint(x: 0.0, y: 0.0)


        let sublayersCount = view.layer.sublayers?.count ?? 0
        view.layer.insertSublayer(gradientLayer, at: UInt32(sublayersCount))

        view.layer.layoutSublayers()
        view.bringSubview(toFront: vwButtonContainer)


    }

    @IBAction func upperColorPicked(_ sender: UIButton) {
        currentUpperColor = sender.backgroundColor!
        setGradientBackground(lowerColor: currentLowerColor, upperColor: currentUpperColor)
    }

    @IBAction func lowerColorPicked(_ sender: UIButton) {
        currentLowerColor = sender.backgroundColor!
        setGradientBackground(lowerColor: currentLowerColor, upperColor: currentUpperColor)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}

Storyboard Layout:

enter image description here

Result:

enter image description here

Upvotes: 1

Sandeep Bhandari
Sandeep Bhandari

Reputation: 20379

Reason

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

Will always insert the gradient layer at index 0. Initially u created a layer with black and white color and added at position 0. Now when user chooses a new color you create a new layer and add it index 0 making black and white layer to be at index 1. Hence only black and white layer will always appear

Solution:

Remove old layer and add new layer.

Step 1:

Declare gradientLayer as instance variable

class ViewController: UIViewController {
    var gradientLayer = CAGradientLayer()
     ....
 }

Step 2:

In IBAction remove old layer and add new layer

@IBAction func upperColorPicked(_ sender: UIButton) {
            self.gradientLayer.removeFromSuperlayer()
            currentUpperColor = sender.backgroundColor!
            setGradientBackground(lowerColor: currentLowerColor, upperColor: currentUpperColor)
        }

        @IBAction func lowerColorPicked(_ sender: UIButton) {
            self.gradientLayer.removeFromSuperlayer()
            currentLowerColor = sender.backgroundColor!
            setGradientBackground(lowerColor: currentLowerColor, upperColor: currentUpperColor)
        }

Solution 2:(Not the preferred one)

If you want to stack up layers with different colors (I don't personally see any reason for doing so, but if thats what you want then) you can use

view.layer.insertSublayer(CALayer(), at: UInt32(view.layer.sublayers?.count ?? 0))

So modify your setGradientBackground as

func setGradientBackground(lowerColor: UIColor, upperColor: UIColor) {

            let gradientLayer = CAGradientLayer()
            gradientLayer.frame = view.bounds
            gradientLayer.colors = [lowerColor.cgColor, upperColor.cgColor]
            gradientLayer.locations = [0.5, 1.0]
            gradientLayer.startPoint = CGPoint(x: 0.0, y: 1.0)
            gradientLayer.endPoint = CGPoint(x: 0.0, y: 0.0)

            view.layer.insertSublayer(CALayer(), at: UInt32(view.layer.sublayers?.count ?? 0))

        }

Upvotes: 4

Related Questions