Reputation: 1436
I have been trying to produce a basic radial gradient background, but without success. I managed to get a linear gradient working as shown with the code below, but I have no idea how to make it radial with different colours - like in the image below. Any help would be greatly appreciated. :)
let gradientLayer: CAGradientLayer = CAGradientLayer()
gradientLayer.colors = gradientColors
gradientLayer.locations = gradientLocations ...
Upvotes: 33
Views: 37945
Reputation: 12598
For years now you simply do this:
class GlowBall: UIView {
private lazy var pulse: CAGradientLayer = {
let l = CAGradientLayer()
l.type = .radial
l.colors = [ UIColor.red.cgColor,
UIColor.yellow.cgColor,
UIColor.green.cgColor,
UIColor.blue.cgColor]
l.locations = [ 0, 0.3, 0.7, 1 ]
l.startPoint = CGPoint(x: 0.5, y: 0.5)
l.endPoint = CGPoint(x: 1, y: 1)
layer.addSublayer(l)
return l
}()
override func layoutSubviews() {
super.layoutSubviews()
pulse.frame = bounds
pulse.cornerRadius = bounds.width / 2.0
}
}
The key lines are:
l.colors = [ UIColor.red.cgColor,
UIColor.yellow.cgColor,
UIColor.green.cgColor,
UIColor.blue.cgColor]
l.locations = [ 0, 0.3, 0.7, 1 ]
Note that you can change the "stretch" as you wish ...
l.locations = [ 0, 0.1, 0.2, 1 ]
Use any colors you like
l.colors = [ UIColor.systemBlue.cgColor,
UIColor.systemPink.cgColor,
UIColor.systemBlue.cgColor,
UIColor.systemPink.cgColor,
UIColor.systemBlue.cgColor,
UIColor.systemPink.cgColor,
UIColor.systemBlue.cgColor,
UIColor.systemPink.cgColor]
l.locations = [ 0,0.1,0.2,0.3,0.4,0.5,0.6,1 ]
It's really that easy now.
Say you want yellow, with a blue band at 0.6:
l.colors = [ UIColor.yellow.cgColor,
UIColor.blue.cgColor,
UIColor.yellow.cgColor]
l.locations = [ 0, 0.6, 1 ]
That works fine.
# yellow...
# blue...
# yellow...
But usually you do this:
# yellow...
# yellow...
# blue...
# yellow...
# yellow...
Notice there are TWO of the yellows at each end ...
l.colors = [ UIColor.yellow.cgColor,
UIColor.yellow.cgColor,
UIColor.blue.cgColor,
UIColor.yellow.cgColor,
UIColor.yellow.cgColor]
In this way, you can control "how wide" the blue band is:
In this example: the blue band will be narrow and sharp:
l.locations = [ 0, 0.58, 0.6, 0.68, 1 ]
In this example the blue band will be broad and soft:
l.locations = [ 0, 0.5, 0.6, 0.7, 1 ]
That is really the secret to how you control gradients, and get the look you want.
Notice this is - very simply - a UIView !!
class GlowBall: UIView { ...
Thus simply
In storyboard, place a UIView where you want
In storyboard, change the class to "GlowBall" instead of UIView
You're done!
Upvotes: 39
Reputation: 4722
A little different approach with function which takes parent view, colors, and locations as input. The function returns a subview where the layer was added. This gives the flexibility to hide/show/remove subview.
override func viewDidLoad() {
super.viewDidLoad()
//squareView is my parent view I am going to add gradient view to it
squareView.backgroundColor = UIColor.black
//Add CG colors
let colours = [UIColor.red.cgColor,UIColor.green.cgColor,UIColor.clear.cgColor]
//Add location with same count as colors, these describe contribution in gradient from center 0 to end 1
let locations:[NSNumber] = [0,0.6,0.8]
//Use gradientView reference to show/hide, remove/re-add from view
let gradientView = self.addGradientViewTo(parentView: self.squareView, colors:colours,locations: locations)
}
func addGradientViewTo (parentView:UIView,colors:[CGColor],locations:[NSNumber]) -> UIView {
//Create customGradientView with exact dimension of parent, add it with centering with parent
let customGradientView = UIView()
customGradientView.backgroundColor = UIColor.clear
customGradientView.frame = parentView.bounds
parentView.addSubview(customGradientView)
customGradientView.centerXAnchor.constraint(equalTo: parentView.centerXAnchor).isActive = true
customGradientView.centerYAnchor.constraint(equalTo: parentView.centerYAnchor).isActive = true
parentView.clipsToBounds = true
//Create layer add it to customGradientView
let gradientLayer = CAGradientLayer()
gradientLayer.type = .radial //Circular
gradientLayer.opacity = 0.8
gradientLayer.colors = colors
gradientLayer.locations = locations
gradientLayer.frame = customGradientView.bounds
//Set start point as center and radius as 1, co-ordinate system maps 0 to 1, 0,0 top left, bottom right 1,1
gradientLayer.startPoint = CGPoint(x: 0.5, y: 0.5)
let radius = 1.0
gradientLayer.endPoint = CGPoint(x: radius, y: radius)
//Add layer at top to make sure its visible
let layerCount:UInt32 = UInt32(customGradientView.layer.sublayers?.count ?? 0)
customGradientView.layer.insertSublayer(gradientLayer, at: layerCount)
customGradientView.layoutIfNeeded()
//Use reference to show/hide add/remove gradient view
return customGradientView
}
Upvotes: 1
Reputation: 10185
@IBDesignable class RadialGradientView: UIView {
@IBInspectable var outsideColor: UIColor = UIColor.red
@IBInspectable var insideColor: UIColor = UIColor.green
override func draw(_ rect: CGRect) {
let colors = [insideColor.cgColor, outsideColor.cgColor] as CFArray
let endRadius = sqrt(pow(frame.width/2, 2) + pow(frame.height/2, 2))
let center = CGPoint(x: bounds.size.width / 2, y: bounds.size.height / 2)
let gradient = CGGradient(colorsSpace: nil, colors: colors, locations: nil)
let context = UIGraphicsGetCurrentContext()
context?.drawRadialGradient(gradient!, startCenter: center, startRadius: 0.0, endCenter: center, endRadius: endRadius, options: CGGradientDrawingOptions.drawsBeforeStartLocation)
}
}
See my full answer here.
Upvotes: 9
Reputation: 2603
Here is an implementation in Swift 3 if you're just looking for a UIView radial gradient background:
class RadialGradientLayer: CALayer {
var center: CGPoint {
return CGPoint(x: bounds.width/2, y: bounds.height/2)
}
var radius: CGFloat {
return (bounds.width + bounds.height)/2
}
var colors: [UIColor] = [UIColor.black, UIColor.lightGray] {
didSet {
setNeedsDisplay()
}
}
var cgColors: [CGColor] {
return colors.map({ (color) -> CGColor in
return color.cgColor
})
}
override init() {
super.init()
needsDisplayOnBoundsChange = true
}
required init(coder aDecoder: NSCoder) {
super.init()
}
override func draw(in ctx: CGContext) {
ctx.saveGState()
let colorSpace = CGColorSpaceCreateDeviceRGB()
let locations: [CGFloat] = [0.0, 1.0]
guard let gradient = CGGradient(colorsSpace: colorSpace, colors: cgColors as CFArray, locations: locations) else {
return
}
ctx.drawRadialGradient(gradient, startCenter: center, startRadius: 0.0, endCenter: center, endRadius: radius, options: CGGradientDrawingOptions(rawValue: 0))
}
}
class RadialGradientView: UIView {
private let gradientLayer = RadialGradientLayer()
var colors: [UIColor] {
get {
return gradientLayer.colors
}
set {
gradientLayer.colors = newValue
}
}
override func layoutSubviews() {
super.layoutSubviews()
if gradientLayer.superlayer == nil {
layer.insertSublayer(gradientLayer, at: 0)
}
gradientLayer.frame = bounds
}
}
Upvotes: 23
Reputation: 10286
Have a look at my implementation of RadialGradientLayer, and feel free to modify it
class RadialGradientLayer: CALayer {
override init(){
super.init()
needsDisplayOnBoundsChange = true
}
init(center:CGPoint,radius:CGFloat,colors:[CGColor]){
self.center = center
self.radius = radius
self.colors = colors
super.init()
}
required init(coder aDecoder: NSCoder) {
super.init()
}
var center:CGPoint = CGPointMake(50,50)
var radius:CGFloat = 20
var colors:[CGColor] = [UIColor(red: 251/255, green: 237/255, blue: 33/255, alpha: 1.0).CGColor , UIColor(red: 251/255, green: 179/255, blue: 108/255, alpha: 1.0).CGColor]
override func drawInContext(ctx: CGContext!) {
CGContextSaveGState(ctx)
var colorSpace = CGColorSpaceCreateDeviceRGB()
var locations:[CGFloat] = [0.0, 1.0]
var gradient = CGGradientCreateWithColors(colorSpace, colors, [0.0,1.0])
var startPoint = CGPointMake(0, self.bounds.height)
var endPoint = CGPointMake(self.bounds.width, self.bounds.height)
CGContextDrawRadialGradient(ctx, gradient, center, 0.0, center, radius, 0)
}
}
In my case I needed it with two colors only and if you need more colors you need to modify location
array declared in drawInContext
. Also after creating object from this class don't forget to call its setNeedsDisplay()
otherwise it wont work. Also sometimes I needed different size gradients so thats why you have to pass radius parameter in initializer and the center point of your gradient
Upvotes: 21