0

I have added a UIView in the storyboard and want to set its background with gradient colors. This is my code

extension UIView {
    func setGradientBackground(colors: [CGColor]) {
        let gradientLayer = CAGradientLayer()
        gradientLayer.colors = colors
        gradientLayer.locations = [0.0, 1.0]
        gradientLayer.frame = self.bounds
        self.layer.insertSublayer(gradientLayer, at:0)
    }
}

and in the viewcontroller,

viewSocial.setGradientBackground(colors: [UIColor.gradientBottomColor.cgColor, UIColor.gradientTopColor.cgColor])

But this creates double gradient layers. see the image

I have tried adding a class for GradientLayer as mentioned here. But this is not allowing to set on a view from the storyboard. Gives the warning of the weak variable.

Krutika Sonawala
  • 1,065
  • 1
  • 12
  • 30
  • where you call setGradientBackground ? – Shehata Gamal Apr 21 '21 at 12:57
  • I’d recommend creating a `UIView` whose `layerClass` is a `CAGradientLayer`. (That’s preferable to adding a `CALayer` subclass as it will respond to layout changes and rotations more gracefully.) And if you make it `@IBDesignable`, you can see this rendered in IB, too. See https://stackoverflow.com/a/37039106/1271826 or https://stackoverflow.com/a/63278350/1271826 – Rob Apr 21 '21 at 13:03

2 Answers2

1

Here is one way to make a @IBDesignable gradient view. It's using the default top-to-bottom gradient direction:

@IBDesignable
class MyGradientView: UIView {
    
    @IBInspectable var color1: UIColor = .red {
        didSet { setNeedsDisplay() }
    }
    @IBInspectable var color2: UIColor = .yellow {
        didSet { setNeedsDisplay() }
    }

    private var gradientLayer: CAGradientLayer!
    
    override class var layerClass: AnyClass {
        return CAGradientLayer.self
    }
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() -> Void {
        // use self.layer as the gradient layer
        gradientLayer = self.layer as? CAGradientLayer
        gradientLayer.colors = [color1.cgColor, color2.cgColor]
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        
        gradientLayer.colors = [color1.cgColor, color2.cgColor]
    }
    
}

It has @IBInspectable vars for "color1" and "color2" ... changing them in the Attributes Inspector will be reflected in Storyboard.

No need for any additional code, such as your current approach with setGradientBackground()

DonMag
  • 69,424
  • 5
  • 50
  • 86
1

Here is a code of @IBDesignable Gradient View. You can use Top to Bottom and Left to Right from Storyboard.

private var startGradientColorAssociatedKey : UIColor = .black
private var endGradientColorAssociatedKey : UIColor = .black
private var observationGradientView: NSKeyValueObservation?

extension UIView {


@IBInspectable var startGradientColor: UIColor {
    get {
        if let color = objc_getAssociatedObject(self, &startGradientColorAssociatedKey) as? UIColor {
            return color
        } else {
            return .black
        }
    } set {
        objc_setAssociatedObject(self, &startGradientColorAssociatedKey, newValue, .OBJC_ASSOCIATION_RETAIN)
    }
}

@IBInspectable var endGradientColor: UIColor {
    get {
        if let color = objc_getAssociatedObject(self, &endGradientColorAssociatedKey) as? UIColor {
            return color
        } else {
            return .black
        }
    } set {
        objc_setAssociatedObject(self, &endGradientColorAssociatedKey, newValue, .OBJC_ASSOCIATION_RETAIN)
    }
}


@IBInspectable
var isTopToBottomGradient: Bool {
    get {
        return self.isTopToBottomGradient
    }
    set {
        DispatchQueue.main.async {
            if newValue {
                self.setGradientBackground(colorTop: self.startGradientColor, colorBottom: self.endGradientColor)
            } else {
                self.setGradientBackground(colorLeft: self.startGradientColor, colorRight: self.endGradientColor)
            }
        }
    }
}


func setGradientBackground(colorLeft: UIColor, colorRight: UIColor) {
    let gradientLayer = CAGradientLayer()
    gradientLayer.colors = [colorLeft.cgColor, colorRight.cgColor]
    gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5)
    gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5)
    gradientLayer.locations = [0, 1]
    gradientLayer.frame = bounds
    observationGradientView = self.observe(\.bounds, options: .new) { [weak gradientLayer] view, change in
        if let value =  change.newValue {
            gradientLayer?.frame = value
        }
    }
    
    layer.insertSublayer(gradientLayer, at: 0)
}
}

Use from Storyboard : If you want to set gradient from top to bottom then set ON isTopToBottomGradient key. Defalt value is OFF

enter image description here