Search code examples
swiftcolorsuiviewgradient

How to apply gradient color in view top left right bottom like this picture?


I want to apply view gradient color but always failed.

I want to apply gradient view like this image:

gradient view top left right bottom

I try with this code:

func gradientColor(topLeft: UIColor, bottomRight: UIColor) {
    let gradient = CAGradientLayer()
    gradient.frame = CGRect(x: 0, y: 0, width: self.mainView.bounds.width, height: self.mainView.bounds.height)
    gradient.startPoint = CGPoint(x: 0.0, y: 0.0)
    gradient.endPoint = CGPoint(x: 1.0, y: 1.0)
    gradient.colors = [topLeft.cgColor, bottomRight.cgColor]
    self.mainView.layer.addSublayer(gradient)
}

but result as empty no color (color did not show) at all if I called like this:

gradientColor(topLeft: UIColor.style.negativeGradient, bottomRight: UIColor.style.negative)

What is the correct code to generate gradient view like that image?


Solution

  • This approach commonly fails because it is called before the views have been laid-out, so self.mainView.bounds is not valid.

    A much more reliable - and flexible - approach is to subclass UIView and have it do all the "background styling" automatically.

    Quick example class:

    class MyCustomGradientView: UIView {
    
        public var gradientColor1: UIColor = .init(red: 0.887, green: 0.993, blue: 0.922, alpha: 1.0) { didSet { gradientLayer.colors = [gradientColor1.cgColor, gradientColor2.cgColor] } }
        public var gradientColor2: UIColor = .white { didSet { gradientLayer.colors = [gradientColor1.cgColor, gradientColor2.cgColor] } }
        
        public var circleColor: UIColor = .init(red: 0.904, green: 0.969, blue: 0.949, alpha: 1.0) { didSet { circleLayer.fillColor = circleColor.cgColor } }
        
        public var circleDiameter: CGFloat = 140.0 { didSet { setNeedsLayout() } }
        public var circleOffset: CGPoint = .init(x: -28.0, y: -2.0) { didSet { setNeedsLayout() } }
        
        override class var layerClass: AnyClass { CAGradientLayer.self }
        private var gradientLayer: CAGradientLayer { layer as! CAGradientLayer }
        
        private let circleLayer: CAShapeLayer = CAShapeLayer()
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            commonInit()
        }
        required init?(coder: NSCoder) {
            super.init(coder: coder)
            commonInit()
        }
        private func commonInit() {
            self.backgroundColor = .clear
            
            gradientLayer.colors = [gradientColor1.cgColor, gradientColor2.cgColor]
            gradientLayer.startPoint = .init(x: 0.0, y: 0.0)
            gradientLayer.endPoint = .init(x: 1.0, y: 1.0)
            
            circleLayer.fillColor = circleColor.cgColor
            layer.addSublayer(circleLayer)
            
            layer.cornerRadius = 16.0
            
            layer.masksToBounds = true
        }
        
        override func layoutSubviews() {
            super.layoutSubviews()
            
            // position the circle so its center is at bottom-right corner
            //  offset circleOffset
            let r: CGRect = .init(x: bounds.maxX - circleDiameter * 0.5, y: bounds.maxY - circleDiameter * 0.5, width: circleDiameter, height: circleDiameter)
                .offsetBy(dx: circleOffset.x, dy: circleOffset.y)
            
            circleLayer.path = UIBezierPath(ovalIn: r).cgPath
        }
        
    }
    

    You are probably doing this:

    mainView = UIView()
    
    // set constraints on mainView
    

    so, instead, we can do this:

    mainView = MyCustomGradientView()
    
    // set constraints on mainView
    

    and we get this (for example):

    enter image description here

    We can add multiple instances, at various sizes:

    enter image description here

    and we can add subviews as we normally would:

    enter image description here

    and set / change colors if desired:

    enter image description here