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
}
}
(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: