skip to Main Content

Currently, I have the following onboarding screen.

enter image description here

I would like to apply some gradient effect, on the bottom of the image.

I want to apply transparent color to black color transition, from 70% (0.7) height of the image, until 100% (1.0) height of the image.

It should look something like the following sample (Look at the bottom of the screen)

enter image description here

I thought the following code might work (I change from clear color to red color, for better visualization on what happens behind the scene)

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    
    // Remove any existing gradient layers to prevent stacking
    demoImageView.layer.sublayers?.forEach { layer in
        if layer is CAGradientLayer {
            layer.removeFromSuperlayer()
        }
    }

    let gradientLayer = CAGradientLayer()

    // Set the gradient colors - from clear to black.
    // We use red so that we can have better visualization to see what's going on.
    gradientLayer.colors = [UIColor.red.cgColor, UIColor.black.cgColor]

    gradientLayer.startPoint = CGPoint(x: 0.5, y: 0.7)
    gradientLayer.endPoint = CGPoint(x: 0.5, y: 1.0)

    // Set the frame of the gradient layer to match the imageView's bounds
    gradientLayer.frame = demoImageView.bounds

    // Add the gradient layer to the imageView's layer
    demoImageView.layer.addSublayer(gradientLayer)
}

The whole image is covered by red (clear color) and black is not seen.

enter image description here

Does anyone has idea what’s wrong with my code. I have try to experiment with gradientLayer.locations.

But, still not able to achieve what I want 😔

Thank you.

2

Answers


  1. This is quite a common need. Let’s invent a gradient view. It covers the main view from the bottom upwards, only to the height of the gradient. The view "hosts" a gradient layer, not as a sublayer, but as its layer, by setting the view’s layerClass to CAGradientLayer.self. We have then but to configure the view’s layer, as follows:

    • Its startPoint and endPoint should be (x: 0.5, y: 0) and (x: 0.5, y: 1) repectively. This will give us a vertical linear gradient flowing from top to bottom.

    • Its colors should be (for our first attempt) an array of two CGColors, one for clear and one for black.

    class GradientView: UIView {
        override class var layerClass: AnyClass { CAGradientLayer.self }
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            isOpaque = false
            if let gradient = self.layer as? CAGradientLayer {
                gradient.startPoint = .init(x: 0.5, y: 0)
                gradient.endPoint = .init(x: 0.5, y: 1)
                gradient.colors = [UIColor.clear.cgColor, UIColor.black.cgColor]
            }
        }
        
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    }
    

    To test it, let’s insert one at the bottom of a view controller’s view:

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        let v = GradientView()
        v.frame = CGRect(x: 0, y: self.view.bounds.height - 100, width: self.view.bounds.width, height: 100)
        self.view.addSubview(v)
        self.view.backgroundColor = .green
    }
    

    Result:

    enter image description here

    Now, if you don’t quite like that, you can play with the specs of the CAGradientLayer. For instance, we could add some more room to the clear part by having three locations (instead of the default, which is two), at let’s say 0.0, 0.3, and 1.0; the colors would then be an array of three colors, clear, clear, and black. We could also add a fourth location to get more black earlier on.

    gradient.locations = [0.0, 0.3, 0.9, 1.0]
    gradient.colors = [UIColor.clear.cgColor, UIColor.clear.cgColor, UIColor.black.cgColor, UIColor.black.cgColor]
    

    Result:

    enter image description here

    That should be enough of an example to let you tweak away to your heart’s content. In real life, of course, you should also generalize this view with a nice API of methods and properties so that at the call site the client can easily change the colors etc.

    Login or Signup to reply.
  2. Quick example, using matt’s GradientView added as a subview to a UIImageView:


    class GradientView: UIView {
        override class var layerClass: AnyClass { CAGradientLayer.self }
        
        override init(frame: CGRect) {
            super.init(frame: frame)
            isOpaque = false
            if let gradient = self.layer as? CAGradientLayer {
                gradient.startPoint = .init(x: 0.5, y: 0)
                gradient.endPoint = .init(x: 0.5, y: 1)
                gradient.colors = [UIColor.clear.cgColor, UIColor.black.cgColor]
            }
        }
        
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    }
    

    Controller with yellow background and image view inset, so we can see the framing:

    class InsetGradOverlayVC: UIViewController {
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            // so we can see the differences
            view.backgroundColor = .systemYellow
            
            let imgView = UIImageView()
            let gradView = GradientView()
            
            imgView.translatesAutoresizingMaskIntoConstraints = false
            gradView.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(imgView)
            imgView.addSubview(gradView)
            
            let g = view.safeAreaLayoutGuide
            NSLayoutConstraint.activate([
                
                imgView.topAnchor.constraint(equalTo: g.topAnchor, constant: 30.0),
                imgView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
                imgView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
                imgView.heightAnchor.constraint(equalTo: imgView.widthAnchor, multiplier: 718.0 / 750.0),
                
                gradView.leadingAnchor.constraint(equalTo: imgView.leadingAnchor),
                gradView.trailingAnchor.constraint(equalTo: imgView.trailingAnchor),
                gradView.bottomAnchor.constraint(equalTo: imgView.bottomAnchor),
                gradView.heightAnchor.constraint(equalTo: imgView.heightAnchor, multiplier: 0.3),
                
            ])
            
            guard let img = UIImage(named: "sample")
            else { fatalError("Could not load image!!!") }
            
            imgView.image = img
            
        }
        
    }
    


    Controller with black background and image view full-width:

    class FullWidthGradOverlayVC: UIViewController {
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            // so we can see the differences
            view.backgroundColor = .black
            
            let imgView = UIImageView()
            let gradView = GradientView()
            
            imgView.translatesAutoresizingMaskIntoConstraints = false
            gradView.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(imgView)
            imgView.addSubview(gradView)
            
            let g = view.safeAreaLayoutGuide
            NSLayoutConstraint.activate([
                
                imgView.topAnchor.constraint(equalTo: g.topAnchor, constant: 30.0),
                imgView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
                imgView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
                imgView.heightAnchor.constraint(equalTo: imgView.widthAnchor, multiplier: 718.0 / 750.0),
                
                gradView.leadingAnchor.constraint(equalTo: imgView.leadingAnchor),
                gradView.trailingAnchor.constraint(equalTo: imgView.trailingAnchor),
                gradView.bottomAnchor.constraint(equalTo: imgView.bottomAnchor),
                gradView.heightAnchor.constraint(equalTo: imgView.heightAnchor, multiplier: 0.3),
                
            ])
            
            guard let img = UIImage(named: "sample")
            else { fatalError("Could not load image!!!") }
            
            imgView.image = img
            
        }
        
    }
    

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search