iosswiftuiviewgradientcagradientlayer

Creating simple left right gradient with top down alpha swift UIView


Would like to create simple custom UIView that fades from red to blue horizontally and then both colors have alphas that fade downwards to zero (so the top of the view is red to blue and the bottom is faded away to clear).

Have tried several simple and complicated routes but cannot get it right.

Sample Case, nesting UIViews (tried to create sub view that has gradient and layer alpha to main view)

class AlphaGradientView299: UIView {

// Main view with vertical alpha gradient
private let mainView: UIView = {
    let view = UIView()
    view.translatesAutoresizingMaskIntoConstraints = false
    return view
}()

// Nested view with left-to-right color gradient
private let nestedView: UIView = {
    let view = UIView()
    view.translatesAutoresizingMaskIntoConstraints = false
    let gradientLayer = CAGradientLayer()
    gradientLayer.frame = view.bounds
    gradientLayer.colors = [UIColor.red.cgColor, UIColor.blue.cgColor]
    gradientLayer.startPoint = CGPoint(x: 0, y: 0.5)
    gradientLayer.endPoint = CGPoint(x: 1, y: 0.5)
    view.layer.addSublayer(gradientLayer)
    return view
}()

override init(frame: CGRect) {
    super.init(frame: frame)
    setupSubviews()
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    setupSubviews()
}

private func setupSubviews() {
    addSubview(mainView)
    mainView.addSubview(nestedView)
    
    NSLayoutConstraint.activate([
        mainView.leadingAnchor.constraint(equalTo: leadingAnchor),
        mainView.topAnchor.constraint(equalTo: topAnchor),
        mainView.trailingAnchor.constraint(equalTo: trailingAnchor),
        mainView.bottomAnchor.constraint(equalTo: bottomAnchor),
        
        nestedView.leadingAnchor.constraint(equalTo: mainView.leadingAnchor),
        nestedView.topAnchor.constraint(equalTo: mainView.topAnchor),
        nestedView.trailingAnchor.constraint(equalTo: mainView.trailingAnchor),
        nestedView.bottomAnchor.constraint(equalTo: mainView.bottomAnchor),
        
       
        ])
    // Set up main view with vertical alpha gradient
    mainView.backgroundColor = .white
    let alphaLayer = CAGradientLayer()
    alphaLayer.frame = mainView.bounds
    alphaLayer.colors = [UIColor.black.withAlphaComponent(1).cgColor,              UIColor.black.withAlphaComponent(0).cgColor]
    alphaLayer.startPoint = CGPoint(x: 0.5, y: 0)
    alphaLayer.endPoint = CGPoint(x: 0.5, y: 1)
    mainView.layer.addSublayer(alphaLayer)
}
}

let testView: AlphaGradientView299 = {
    let v = AlphaGradientView299()
    return v
}()
self.view.addSubview(testView)
    testView.translatesAutoresizingMaskIntoConstraints = false
    testView.topAnchor.constraint(equalTo: topAnchor).isActive = true
    testView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
    testView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
testView.heightAnchor.constraint(equalToConstant: 30).isActive = true

The above fails to even load a view.

Here's a different attempt using only CAGradientLayers:

class CustomGradientView100: UIView {

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

var gradientLayer: CAGradientLayer {
    return self.layer as! CAGradientLayer
}

override init(frame: CGRect) {
    super.init(frame: frame)
    setupGradientLayer()
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    setupGradientLayer()
}

private func setupGradientLayer() {
    gradientLayer.frame = bounds
    gradientLayer.colors = [UIColor.red.cgColor, UIColor.blue.cgColor]
    gradientLayer.locations = [0.0, 1.0]
    gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5)
    gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5)
    
    let alphaGradientLayer = CAGradientLayer()
    alphaGradientLayer.colors = [UIColor.black.cgColor, UIColor.black.cgColor]
    alphaGradientLayer.locations = [0.0, 1.0]
    alphaGradientLayer.startPoint = CGPoint(x: 0.5, y: 0.0)
    alphaGradientLayer.endPoint = CGPoint(x: 0.5, y: 1.0)
    alphaGradientLayer.frame = gradientLayer.bounds
    gradientLayer.insertSublayer(alphaGradientLayer, at: 0)
    self.layer.insertSublayer(alphaGradientLayer, at: 0)
    
}
}

Used black to see what was going on better but the above only creates a left right gradient, no top down fade.

Please help, am losing my mind

Edit (Lazarevzubov's View recreated as custom UIView Class):

class lazaView: UIView {

private var fadeLayer: CAGradientLayer!
  private var gradientLayer: CAGradientLayer!
  private var gradientView: UIView!

override init(frame: CGRect) {
    super.init(frame: frame)
    setupGradient()
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    setupGradient()
}

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

private func setupGradient() {
    
    gradientView = UIView()
        gradientView.backgroundColor = .red
        gradientView.translatesAutoresizingMaskIntoConstraints = false

        addSubview(gradientView)
        NSLayoutConstraint.activate([
          gradientView.topAnchor.constraint(equalTo: topAnchor),
          gradientView.bottomAnchor.constraint(equalTo: bottomAnchor),
          gradientView.leadingAnchor.constraint(equalTo: leadingAnchor),
          gradientView.trailingAnchor.constraint(equalTo: trailingAnchor)
        ])

        gradientLayer = CAGradientLayer()
        gradientLayer.colors = [UIColor.red.cgColor, UIColor.blue.cgColor]
        gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5)
        gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5)
        gradientLayer.locations = [0, 1]

        gradientView.layer.addSublayer(gradientLayer)

        fadeLayer = CAGradientLayer()
        fadeLayer.colors = [UIColor.clear.cgColor, UIColor.white.cgColor]
        fadeLayer.startPoint = CGPoint(x: 0.5, y: 0.0)
        fadeLayer.endPoint = CGPoint(x: 0.5, y: 1.0)
        fadeLayer.locations = [0, 1]

        gradientView.layer.addSublayer(fadeLayer)
    
    gradientLayer.frame = gradientView.bounds
        fadeLayer.frame = gradientView.bounds
    
}

}


Solution

  • (This is an edited answer, because at first, I missed the part with fading out.)

    Here's an example of a UIView with a red-blue horizontal gradient:

    final class GradientView: UIView {
    
      override func layoutSubviews() {
        super.layoutSubviews()
        setupGradient()
      }
    
      private func setupGradient() {
        backgroundColor = .clear
    
        let gradientLayer = CAGradientLayer()
        gradientLayer.colors = [UIColor.red.cgColor, UIColor.blue.cgColor]
        gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5)
        gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5)
        gradientLayer.frame = bounds
    
        layer.addSublayer(gradientLayer)
      }
    
    }
    

    To make this view fade away, we can add a mask layer to it, like this:

    final class GradientView: UIView {
    
      override func layoutSubviews() {
        super.layoutSubviews()
        setup()
      }
    
      private func setup() {
        backgroundColor = .clear
    
        let gradientLayer = CAGradientLayer()
        gradientLayer.colors = [UIColor.red.cgColor, UIColor.blue.cgColor]
        gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5)
        gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5)
        gradientLayer.frame = bounds
    
        layer.addSublayer(gradientLayer)
    
        // Here:
    
        let maskLayer = CAGradientLayer()
        maskLayer.colors = [UIColor.white.cgColor, UIColor.clear.cgColor]
        maskLayer.startPoint = CGPoint(x: 0.5, y: 0.0)
        maskLayer.endPoint = CGPoint(x: 0.5, y: 1.0)
        maskLayer.frame = bounds
    
        layer.mask = maskLayer
      }
    
    }
    

    Here's a sample usage in a UIViewController (which has a green background as a proof of the view's transparency):

    final class ViewController: UIViewController {
    
      override func viewDidLoad() {
        super.viewDidLoad()
    
        view.backgroundColor = .green
    
        let gradientView = GradientView()
        gradientView.translatesAutoresizingMaskIntoConstraints = false
    
        view.addSubview(gradientView)
        NSLayoutConstraint.activate([
          gradientView.topAnchor.constraint(equalTo: view.topAnchor),
          gradientView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
          gradientView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
          gradientView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
        ])
      }
    
    }
    

    And the result:

    enter image description here