Fill a CAShapeLayer with Gradient in swift? - ios

I have filled the circle boundary with CAShapeLayer.Now i am giving my CAShape color as red but i want to give it as a gradient color.Please tell me how can i do this?
circlePath = UIBezierPath(arcCenter: centerPoint, radius: circleRadius, startAngle: CGFloat(1.5 * M_PI), endAngle: CGFloat(-0.5 * M_PI), clockwise: false)
//add gradient to the below
progressCircle = CAShapeLayer ()
progressCircle.path = circlePath?.cgPath
progressCircle.strokeColor = UIColor.red.cgColor
progressCircle.fillColor = UIColor.clear.cgColor
progressCircle.lineWidth = 4.0
progressCircle.strokeStart = 0
progressCircle.strokeEnd = 0.7
let gradient: CAGradientLayer = CAGradientLayer()
let startingColorOfGradient = UIColor(colorLiteralRed: 50/255, green: 189/255, blue: 170/255, alpha: 1.0).cgColor
let endingColorOFGradient = UIColor(colorLiteralRed: 133/255, green: 210/255, blue: 230/255, alpha: 1.0).cgColor
gradient.startPoint = CGPoint(x: 1.0, y: 0.5)
gradient.endPoint = CGPoint(x: 0.0, y: 0.5)
gradient.colors = [startingColorOfGradient , endingColorOFGradient]
circle.layer.addSublayer(progressCircle)
circle.layer.addSublayer(gradient)

I think you're missing the gradient frame, the gradient locations and you should add it to the layer's mask. The following example works for me:
let gradient = CAGradientLayer()
gradient.startPoint = CGPoint(x: 1, y: 0.5)
gradient.endPoint = CGPoint(x: 0, y: 0.5)
gradient.frame = CGRect(x: 0, y: 0, width: yourWidth, height: yourHeight)
gradient.colors = [
UIColor(white: 1, alpha: 0.1).cgColor,
UIColor(white: 1, alpha: 0.5).cgColor,
UIColor(white: 1, alpha: 0.9).cgColor
]
gradient.locations = [
0.0,
0.5,
1.0
]
circle.layer.mask = gradient
Hope this helps!

Related

NavBar title hidden behind CAGradientLayer

I would like to add gradient layer to my navbar, but after I add it, layer hides my title, like its behind that new sublayer.
override func viewDidLoad() {
super.viewDidLoad()
self.title = "My Trips"
self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
//self.navigationController?.navigationBar.layer.addSublayer(gradientLayer)
self.navigationController?.navigationBar.layer.insertSublayer(gradientLayer, at: 1)
setupGradient()
}
let gradientLayer = CAGradientLayer()
func setupGradient() {
gradientLayer.colors = [
UIColor(red: 0.259, green: 0.188, blue: 0.475, alpha: 1).cgColor,
UIColor(red: 0.11, green: 0.024, blue: 0.369, alpha: 1).cgColor
]
gradientLayer.locations = [0, 1]
gradientLayer.startPoint = CGPoint(x: 0.25, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 0.75, y: 0.5)
gradientLayer.transform = CATransform3DMakeAffineTransform(CGAffineTransform(a: 0, b: 0.58, c: -0.58, d: 0, tx: 0.5, ty: 0))
gradientLayer.bounds = (self.navigationController?.navigationBar.bounds.insetBy(dx: -0.01*view.bounds.size.width, dy: -1*view.bounds.size.height))!
}
Use this line for inserting layer.
self.navigationController?.navigationBar.subviews.first?.layer.insertSublayer(gradientLayer, at: 0)

Attempting to use FBShimmer cocoa pod on iOS but not finding Foundation framework

I am Attempting to use FBShimmer cocoa pod on iOS but not finding Foundation framework.PodFile shown here
I ended up just using a UIView extension to achieve the same effect. Much less code and no external dependency.
func startShimmering() {
let light = UIColor(red: 0, green: 0, blue: 0, alpha: 0.5).cgColor
let dark = UIColor.white.cgColor
let gradient: CAGradientLayer = CAGradientLayer()
gradient.colors = [dark, light, dark]
gradient.frame = CGRect(x: -self.bounds.size.width, y: 0, width: 3*self.bounds.size.width, height: self.bounds.size.height)
gradient.startPoint = CGPoint(x: 0.0, y: 0.4)
gradient.endPoint = CGPoint(x: 1.0, y: 0.5)
gradient.locations = [0.4, 0.5, 0.6]
self.layer.mask = gradient
let animation: CABasicAnimation = CABasicAnimation(keyPath: "locations")
animation.fromValue = [0.0, 0.1, 0.2]
animation.toValue = [0.8, 0.9, 1.0]
animation.duration = 3.0
animation.repeatCount = HUGE
gradient.add(animation, forKey: "shimmer")
}

How to add gradient color to a CAShapeLayer created using UIBezierPath

I have tried this and this but still no success. Basically I want to create something like this (inner circle). Circle will be created according to some data lets say if data is 50 we will get a half circle and if its 100 we will get full circle.
And this is what I have so far, so how can I create the above design
I have created a View using interface builder, and drawn these circle using this code
override func viewDidLoad() {
let circlePath = UIBezierPath(arcCenter: CGPoint(x: myView.layer.frame.height/2,y: myView.layer.frame.height/2), radius: CGFloat(100), startAngle: CGFloat(0), endAngle:CGFloat(M_PI * 2), clockwise: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = circlePath.CGPath
shapeLayer.fillColor = UIColor.clearColor().CGColor
shapeLayer.strokeColor = UIColor.grayColor().CGColor
shapeLayer.lineWidth = 3.0
let colorCirclePath = UIBezierPath(arcCenter: CGPoint(x: myView.layer.frame.height/2,y: myView.layer.frame.height/2), radius: CGFloat(100), startAngle: CGFloat(0), endAngle:CGFloat(M_PI * 2), clockwise: true)
let coloredShapeLayer = CAShapeLayer()
coloredShapeLayer.path = colorCirclePath.CGPath
coloredShapeLayer.fillColor = UIColor.clearColor().CGColor
coloredShapeLayer.strokeColor = UIColor.whiteColor().CGColor
coloredShapeLayer.lineWidth = 10.0
self.myView.layer.addSublayer(coloredShapeLayer)
self.myView.layer.addSublayer(shapeLayer)
}
This is how I am creating the gradient.
let gradient: CAGradientLayer = CAGradientLayer()
let startingColorOfGradient = UIColor(colorLiteralRed: 50/255, green: 189/255, blue: 170/255, alpha: 1.0).CGColor
let endingColorOFGradient = UIColor(colorLiteralRed: 133/255, green: 210/255, blue: 230/255, alpha: 1.0).CGColor
gradient.startPoint = CGPoint(x: 1.0, y: 0.5)
gradient.endPoint = CGPoint(x: 0.0, y: 0.5)
gradient.colors = [startingColorOfGradient , endingColorOFGradient]
self.myView.layer.insertSublayer(gradient, atIndex:0)
If you want a real simulation of gradient color you can check this SO answer .It's based on a cross that divided your rectangular view in 4 portions, then shift the colors on each portion to obtain a regular gradient applied to the layer mask.

How to Add a CAGradientLayer to a CAShapeLayer?

I am trying to add a CAGradientLayer to a CAShapeLayer, but the gradient is not conforming to the shape of the layer. Instead, the gradient is taking the shape of the UIView itself. I originally attempted this with a UIBezierPath but it did not work either. I am trying to put a gradient into my shape.
override func drawRect(rect: CGRect) {
let bounds = CGRect(x: 0, y: 0, width: self.bounds.width, height: self.bounds.height)
let center = self.center
let path = CAShapeLayer()
path.bounds = bounds
path.position = CGPoint(x: center.x, y: center.y)
//path.backgroundColor = UIColor.redColor().CGColor
path.cornerRadius = 20
//self.layer.addSublayer(path)
let gradientLayer = CAGradientLayer()
gradientLayer.frame = path.bounds
gradientLayer.colors = [cgColorForRed(209.0, green: 0.0, blue: 0.0),
cgColorForRed(255.0, green: 102.0, blue: 34.0),
cgColorForRed(255.0, green: 218.0, blue: 33.0),
cgColorForRed(51.0, green: 221.0, blue: 0.0),
cgColorForRed(17.0, green: 51.0, blue: 204.0),
cgColorForRed(34.0, green: 0.0, blue: 102.0),
cgColorForRed(51.0, green: 0.0, blue: 68.0)]
gradientLayer.startPoint = CGPoint(x:0,y:0)
gradientLayer.endPoint = CGPoint(x:0, y:1)
path.insertSublayer(gradientLayer, atIndex: 1)
self.layer.addSublayer(path)
}
func cgColorForRed(red: CGFloat, green: CGFloat, blue: CGFloat ) -> AnyObject {
return UIColor(red: red/255.0, green: green/255.0, blue: blue/255.5, alpha: 1.0).CGColor as AnyObject
}
Originally I had path defined to be a UIBezierPath of a rectangle with rounded corners which I am looking to fill with the gradient:
let insetRect = CGRectInset(rect, lineWidth / 2, lineWidth / 2)
let path = UIBezierPath(roundedRect: insetRect, cornerRadius: 10)
fillColor.setFill()
path.fill()
path.lineWidth = self.lineWidth
UIColor.blackColor().setStroke()
path.stroke()
Edit from Larcerax's response:
class Thermometer: UIView {
//#IBInspectable var fillColor: UIColor = UIColor.greenColor()
let lineWidth: CGFloat = 2
override func drawRect(rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
let svgid = CGGradientCreateWithColors(CGColorSpaceCreateDeviceRGB(), [cgColorForRed(209.0, green: 0.0, blue: 0.0),
cgColorForRed(255.0, green: 102.0, blue: 34.0),
cgColorForRed(255.0, green: 218.0, blue: 33.0),
cgColorForRed(51.0, green: 221.0, blue: 0.0),
cgColorForRed(17.0, green: 51.0, blue: 204.0),
cgColorForRed(34.0, green: 0.0, blue: 102.0),
cgColorForRed(51.0, green: 0.0, blue: 68.0)], [0,1])
CGContextSaveGState(context)
let insetRect = CGRectInset(rect, lineWidth / 2, lineWidth / 2)
let path = UIBezierPath(roundedRect: insetRect, cornerRadius: 10)
path.addClip()
CGContextDrawLinearGradient(context, svgid, CGPoint(x: 0, y: path.bounds.height),
CGPoint(x: path.bounds.width, y: path.bounds.height),
UInt32(kCGGradientDrawsBeforeStartLocation) | UInt32(kCGGradientDrawsAfterEndLocation))
CGContextRestoreGState(context)
}
You can try something like the following set up in order to produce the following shape, this is just an example to show you the setup, but this should do it:
let context = UIGraphicsGetCurrentContext()
let gradientColor3 = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)
let gradientColor4 = UIColor(red: 0.000, green: 1.000, blue: 0.000, alpha: 1.000)
let sVGID_1_2 = CGGradientCreateWithColors(CGColorSpaceCreateDeviceRGB(), [gradientColor3.CGColor, gradientColor4.CGColor], [0, 1])
CGContextSaveGState(context)
CGContextTranslateCTM(context, 198.95, 199.4)
CGContextRotateCTM(context, -30 * CGFloat(M_PI) / 180)
var polygonPath = UIBezierPath()
polygonPath.moveToPoint(CGPointMake(0, -145.95))
polygonPath.addLineToPoint(CGPointMake(126.4, -72.98))
polygonPath.addLineToPoint(CGPointMake(126.4, 72.97))
polygonPath.addLineToPoint(CGPointMake(0, 145.95))
polygonPath.addLineToPoint(CGPointMake(-126.4, 72.98))
polygonPath.addLineToPoint(CGPointMake(-126.4, -72.97))
polygonPath.closePath()
CGContextSaveGState(context)
polygonPath.addClip()
CGContextDrawLinearGradient(context, sVGID_1_2,
CGPointMake(-126.4, -72.97),
CGPointMake(126.41, 72.99),
UInt32(kCGGradientDrawsBeforeStartLocation) | UInt32(kCGGradientDrawsAfterEndLocation))
CGContextRestoreGState(context)
to produce this:
If this doesn't work, then tell me, I will try to fix it up to make it work.
Also, here's just a simple rectangle:
let context = UIGraphicsGetCurrentContext()
let gradientColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)
let gradientColor2 = UIColor(red: 0.988, green: 0.933, blue: 0.129, alpha: 1.000)
let gradientColor3 = UIColor(red: 1.000, green: 0.000, blue: 1.000, alpha: 1.000)
let sVGID_1_3 = CGGradientCreateWithColors(CGColorSpaceCreateDeviceRGB(), [gradientColor.CGColor, gradientColor2.CGColor, gradientColor3.CGColor], [0, 0.43, 1])
let rectanglePath = UIBezierPath(rect: CGRectMake(68, 28, 78.4, 78.4))
CGContextSaveGState(context)
rectanglePath.addClip()
CGContextDrawLinearGradient(context, sVGID_1_3,
CGPointMake(68, 67.19),
CGPointMake(146.38, 67.19),
UInt32(kCGGradientDrawsBeforeStartLocation) | UInt32(kCGGradientDrawsAfterEndLocation))
CGContextRestoreGState(context)
here's a function to try out, you'll have to mess around with the colors, but you can input a frame:
class func drawStuff(#frame: CGRect) {
let context = UIGraphicsGetCurrentContext()
let gradientColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)
let gradientColor2 = UIColor(red: 0.988, green: 0.933, blue: 0.129, alpha: 1.000)
let gradientColor3 = UIColor(red: 1.000, green: 0.000, blue: 1.000, alpha: 1.000)
let sVGID_1_4 = CGGradientCreateWithColors(CGColorSpaceCreateDeviceRGB(), [gradientColor.CGColor, gradientColor2.CGColor, gradientColor3.CGColor], [0, 0.43, 1])
let rectangleRect = CGRectMake(frame.origin.x, frame.origin.y, frame.size.width, frame.size.height)
let rectanglePath = UIBezierPath(rect: rectangleRect)
CGContextSaveGState(context)
rectanglePath.addClip()
CGContextDrawLinearGradient(context, sVGID_1_4, CGPointMake(rectangleRect.minX, rectangleRect.midY),CGPointMake(rectangleRect.maxX, rectangleRect.midY), 0)
CGContextRestoreGState(context)
}
Or you can just use this written by myself :) https://github.com/BilalReffas/SwiftyGradient
Swift 4 version of the first shape from #Loxx answer above which produces this output:
.
private func drawShape() {
guard let context = UIGraphicsGetCurrentContext() else {
print("No context, handle me")
return
}
let gradientColor3 = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)
let gradientColor4 = UIColor(red: 0.000, green: 1.000, blue: 0.000, alpha: 1.000)
guard let sVGID_1_2 = CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(), colors: [gradientColor3.cgColor, gradientColor4.cgColor] as CFArray, locations: [0, 1]) else {
print("No gradient, handle me")
context.restoreGState()
return
}
context.saveGState()
context.translateBy(x: 198.95, y: 199.4)
context.rotate(by: -30 * CGFloat(Double.pi) / 180)
let polygonPath = UIBezierPath()
polygonPath.move(to: CGPoint(x: 0, y: -145.95))
polygonPath.addLine(to: CGPoint(x: 126.4, y: -72.98))
polygonPath.addLine(to: CGPoint(x: 126.4, y: 72.97))
polygonPath.addLine(to: CGPoint(x: 0, y: 145.95))
polygonPath.addLine(to: CGPoint(x: -126.4, y: 72.98))
polygonPath.addLine(to: CGPoint(x: -126.4, y: -72.97))
polygonPath.close()
context.saveGState()
polygonPath.addClip()
context.drawLinearGradient(sVGID_1_2,
start: CGPoint(x: -126.4, y: -72.97),
end: CGPoint(x: 126.41, y: 72.99),
options: [.drawsBeforeStartLocation, .drawsAfterEndLocation])
context.restoreGState()
}
I'm posting it because basically every line has changed in conversion to Swift 4 and it took a while to convert it just to check if it works.

Gradiant in playground different from simulator

I tried to make a gradiant using playground then make it into a subclass of uiview to use it in my codebase.
The playground code is like this:
let view = UIView(frame: CGRect(x: 0, y: 0, width: 640, height: 41
))
let gradient: CAGradientLayer = CAGradientLayer(layer: view.layer)
gradient.frame = view.frame
let lightColor = UIColor(red: 157/256, green: 157/256, blue: 154/256, alpha: 1)
let darkColor = UIColor(red: 108/256, green: 104/256, blue: 104/256, alpha: 1)
gradient.colors = [lightColor.CGColor, darkColor.CGColor]
gradient.locations = [0.6 , 0.4]
gradient.startPoint = CGPoint(x: 1, y: 0.55)
gradient.endPoint = CGPoint(x: 0, y: 0.45)
view.layer.insertSublayer(gradient, atIndex: 0)
view //displayed
And display like this:
I tried to make it into my codebase with:
class GradiantView : UIView {
override var frame: CGRect {
didSet {
let gradient: CAGradientLayer = CAGradientLayer(layer: layer)
gradient.frame = bounds
let lightColor = UIColor(red: 157/256, green: 157/256, blue: 154/256, alpha: 1)
let darkColor = UIColor(red: 108/256, green: 104/256, blue: 104/256, alpha: 1)
gradient.colors = [lightColor.CGColor, darkColor.CGColor]
gradient.locations = [0.6 , 0.4]
gradient.startPoint = CGPoint(x: 1, y: 0.55)
gradient.endPoint = CGPoint(x: 0, y: 0.45)
layer.insertSublayer(gradient, atIndex: 0)
}
}
}
However, the result is like this: (no gradiant applied)
What happened and how to fix it ?
I inverted the locations and it seems to work.
I welcome any explanation on the difference between simulator and playground.
gradientLayer.locations = [0.4 , 0.6]

Resources