Core Graphics- Using CGGradient with strokes and text - ios

So here is my code for creating a particular gradient:
let newBlueOne = UIColor(red: 0.141, green: 0.776, blue: 0.863, alpha: 1.000)
let newBlueTwo = UIColor(red: 0.318, green: 0.290, blue: 0.616, alpha: 1.000)
And here is the code for the gradient itself:
let newBlue = CGGradientCreateWithColors(CGColorSpaceCreateDeviceRGB(), [newBlue1.CGColor, newBlue2.CGColor], [0, 1])!
Here is the code for the rectangle I'm trying to make:
let rectanglePath = UIBezierPath(roundedRect: CGRect(x: 22, y: 35, width: 194, height: 38), cornerRadius: 19)
CGContextSaveGState(context)
rectanglePath.addClip()
CGContextDrawLinearGradient(context, translucent1, CGPoint(x: 119, y: 35), CGPoint(x: 119, y: 73), CGGradientDrawingOptions())
CGContextRestoreGState(context)
rectanglePath.lineWidth = 1
rectanglePath.stroke()
I want to use that newBlue gradient above as the stroke colour for this rectangle created. I also want to change the colour of some text to that newBlue gradient. Unfortunately, I can't quite figure it out. Can anyone help?
I appreciate your responses and look forward to reading them. Thank you so much :)

You can not directly stroke a path with a gradient.
To do what you want, you will need to go to the Core Graphics Layer, and use the CGContext Functions.
First, you create your path, and set its various properties, such as line weight.
Then, you use the CGContextReplacePathWithStrokedPath(context) function. Look it up in the CGContext documentation.
Then, you use the resultant path as a clip path.
And then, you paint that area with your gradient.
Like This:
override func drawRect(rect: CGRect) {
let newBlueOne = UIColor(red: 0.141, green: 0.776, blue: 0.863, alpha: 1.000)
let newBlueTwo = UIColor(red: 0.318, green: 0.290, blue: 0.616, alpha: 1.000)
let newBlue = CGGradientCreateWithColors(CGColorSpaceCreateDeviceRGB(), [newBlueOne.CGColor, newBlueTwo.CGColor], [0, 1])!
let lineWeight: CGFloat = 20.0
let context: CGContext = UIGraphicsGetCurrentContext()!
let rect: CGRect = CGRectMake(40.0, 40.0, 200.0, 200.0)
CGContextAddRect(context, rect)
CGContextSetLineWidth(context, lineWeight)
CGContextReplacePathWithStrokedPath(context)
CGContextClip(context)
let startPoint: CGPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect))
let endPoint: CGPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect))
CGContextDrawLinearGradient(context, newBlue, startPoint, endPoint, CGGradientDrawingOptions(rawValue: 0))
}
I only addressed painting the border. I don't know about the text.

Related

Why is CILinearGradient resulting in a very NON-linear gradient?

I'm a relatively new Swift developer and I am using the CILinearGradient CIFilter to generate gradients that I can then use as backgrounds and textures. I was pretty happy with the way it was working, until I realized that the gradients coming out of it seem to be heavily skewed towards away from the black end of the spectrum.
At first I thought I was nuts, but then I created pure black-to-white and white-to-black gradients and put them on screen next to each other. I took a screenshot and brought it into Photoshop. then I looked at the color values. You can see that the ends of each gradient line up (pure black over pure white on one end, and the opposite on the other), but the halfway point of each gradient is significantly skewed towards the black end.
Is this an issue with the CIFilter or am I doing something wrong? Thanks to anyone with any insight on this!
Here's my code:
func gradient2colorIMG(UIcolor1: UIColor, UIcolor2: UIColor, width: CGFloat, height: CGFloat) -> CGImage? {
if let gradientFilter = CIFilter(name: "CILinearGradient") {
let startVector:CIVector = CIVector(x: 0 + 10, y: 0)
let endVector:CIVector = CIVector(x: width - 10, y: 0)
let color1 = CIColor(color: UIcolor1)
let color2 = CIColor(color: UIcolor2)
let context = CIContext(options: nil)
if let currentFilter = CIFilter(name: "CILinearGradient") {
currentFilter.setValue(startVector, forKey: "inputPoint0")
currentFilter.setValue(endVector, forKey: "inputPoint1")
currentFilter.setValue(color1, forKey: "inputColor0")
currentFilter.setValue(color2, forKey: "inputColor1")
if let output = currentFilter.outputImage {
if let cgimg = context.createCGImage(output, from: CGRect(x: 0, y: 0, width: width, height: height)) {
let gradImage = cgimg
return gradImage
}
}
}
}
return nil
}
and then I call it in SpriteKit using this code (but this is just so I can see them on the screen to compare the CGImages that are output by the function) ...
if let gradImage = gradient2colorIMG(UIcolor1: UIColor(red: 255.0 / 255.0, green: 255.0 / 255.0, blue: 255.0 / 255.0, alpha: 1.0), UIcolor2: UIColor(red: 0.0 / 255.0, green: 0.0 / 255.0, blue: 0.0 / 255.0, alpha: 1.0), width: 250, height: 80) {
let sampleback = SKShapeNode(path: CGPath(roundedRect: CGRect(x: 0, y: 0, width: 250, height: 80), cornerWidth: 5, cornerHeight: 5, transform: nil))
sampleback.fillColor = .white
sampleback.fillTexture = SKTexture(cgImage: gradImage)
sampleback.zPosition = 200
sampleback.position = CGPoint(x: 150, y: 50)
self.addChild(sampleback)
}
if let gradImage2 = gradient2colorIMG(UIcolor1: UIColor(red: 0.0 / 255.0, green: 0.0 / 255.0, blue: 0.0 / 255.0, alpha: 1.0), UIcolor2: UIColor(red: 255.0 / 255.0, green: 255.0 / 255.0, blue: 255.0 / 255.0, alpha: 1.0), width: 250, height: 80) {
let sampleback2 = SKShapeNode(path: CGPath(roundedRect: CGRect(x: 0, y: 0, width: 250, height: 80), cornerWidth: 5, cornerHeight: 5, transform: nil))
sampleback2.fillColor = .white
sampleback2.fillTexture = SKTexture(cgImage: gradImage2)
sampleback2.zPosition = 200
sampleback2.position = CGPoint(x: 150, y: 150)
self.addChild(sampleback2)
}
As another follow-up, I tried doing a red-blue gradient (so purely a change in hue) and it is perfectly linear (see below). The issue seems to be around the brightness.
A red-blue gradient DOES ramp its hue in a perfectly linear fashion
Imagine that black is 0 and white is 1. Then the problem here is that we intuitively think that 50% of black "is" a grayscale value of 0.5 — and that is not true.
To see this, consider the following core image experiment:
let con = CIContext(options: nil)
let white = CIFilter(name:"CIConstantColorGenerator")!
white.setValue(CIColor(color:.white), forKey:"inputColor")
let black = CIFilter(name:"CIConstantColorGenerator")!
black.setValue(CIColor(color:UIColor.black.withAlphaComponent(0.5)),
forKey:"inputColor")
let atop = CIFilter(name:"CISourceAtopCompositing")!
atop.setValue(white.outputImage!, forKey:"inputBackgroundImage")
atop.setValue(black.outputImage!, forKey:"inputImage")
let cgim = con.createCGImage(atop.outputImage!,
from: CGRect(x: 0, y: 0, width: 201, height: 50))!
let image = UIImage(cgImage: cgim)
let iv = UIImageView(image:image)
self.view.addSubview(iv)
iv.frame.origin = CGPoint(x: 100, y: 150)
What I've done here is to lay a 50% transparency black swatch on top of a white swatch. We intuitively imagine that the result will be a swatch that will read as 0.5. But it isn't; it's 0.737, the very same shade that is appearing at the midpoint of your gradients:
The reason is that everything here is happening, not in some mathematical vacuum, but in a color space adjusted for a specific gamma.
Now, you may justly ask: "But where did I specify this color space? This is not what I want!" Aha. You specified it in the first line, when you created a CIContext without overriding the default working color space.
Let's fix that. Change the first line to this:
let con = CIContext(options: [.workingColorSpace : NSNull()])
Now the output is this:
Presto, that's your 0.5 gray!
So what I'm saying is, if you create your CIContext like that, you will get the gradient you are after, with 0.5 gray at the midpoint. I'm not saying that that is any more "right" than the result you are getting, but at least it shows how to get that particular result with the code you already have.
(In fact, I think what you were getting originally is more "right", as it is adjusted for human perception.)
The midpoint of the CILinearGradient appears to correspond to 188, 188, 188, which looks like the “absolute whiteness” rendition of middle gray, which is not entirely unreasonable. (The CISmoothLinearGradient offers a smoother transition, but it doesn’t have the midpoint at 0.5, 0.5, 0.5, either.) As an aside, the “linear” in CILinearGradient and CISmoothLinearGradient refer to the shape of the gradient (to differentiate it from a “radial” gradient), not the nature of the color transitions within the gradient.
However if you want a gradient whose midpoint is 0.5, 0.5, 0.5, you can use CGGradient:
func simpleGradient(in rect: CGRect) -> UIImage {
return UIGraphicsImageRenderer(bounds: rect).image { context in
let colors = [UIColor.white.cgColor, UIColor.black.cgColor]
let colorSpace = CGColorSpaceCreateDeviceGray() // or RGB works, too
guard let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: nil) else { return }
context.cgContext.drawLinearGradient(gradient, start: .zero, end: CGPoint(x: rect.maxX, y: 0), options: [])
}
}
Alternatively, if you want a gradient background, you might define a UIView subclass that uses a CAGradientLayer as its backing layer:
class GradientView: UIView {
override class var layerClass: AnyClass { return CAGradientLayer.self }
var gradientLayer: CAGradientLayer { return layer as! CAGradientLayer }
override init(frame: CGRect = .zero) {
super.init(frame: frame)
configure()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
configure()
}
func configure() {
gradientLayer.colors = [UIColor.white.cgColor, UIColor.black.cgColor]
gradientLayer.startPoint = CGPoint(x: 0, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 1, y: 0.5)
}
}

Apply CAGradientLayer to layer.borderColor

I'm trying to implement a underlined UITextField with a gradient. Therefore I created a extension with a function underlined().
To get a gradient, I created a CAGradientLayer and made these customizations:
func underlined(){
let color = UIColor(red: 11/255, green: 95/255, blue: 244/255, alpha: 1).cgColor
let sndColor = UIColor(red: 106/255, green: 178/255, blue: 255/255, alpha: 1).cgColor
let gradient: CAGradientLayer = CAGradientLayer()
gradient.colors = [color, sndColor]
gradient.locations = [0.0, 1.0]
gradient.startPoint = CGPoint(x: 0, y: 0)
gradient.endPoint = CGPoint(x: 1, y: 0)
let width = CGFloat(2.0)
gradient.frame = CGRect(x: 0, y: self.frame.size.height - width, width: self.frame.size.width, height: self.frame.size.height)
gradient.borderWidth = width
self.layer.insertSublayer(gradient, at: 0)
self.layer.masksToBounds = true
}
A underline is being displayed, but it solely solid black - I've tried to change the colors, but it remains black (Issue outdated - see edit).
Does anybody see the issue?
Edit:
Adding gradient.borderColor = UIColor.blue.cgColor let me change the color of the line - but how can I apply the gradient on the border color?
Remove the below line from your code:
gradient.borderWidth = width
Screenshot:
Your code was not working because, the borderWidth is covering whole of the gradient frame.Try setting the borderColor, then you'll see the difference.
gradient.borderColor = UIColor.red.cgColor
Let me know if you still face any issues.

How to implement a black gradient on an image in iOS using swift

I am working on an iOS project where I have white labels on bright images. The problem is for bright images the white labels are not showing. Here is an example:
Label not showing: https://imgur.com/hKtejHn
Label showing: https://imgur.com/Ef5qJAh
I think if I add a black gradient on all the image then the white labels will be visible. Can anyone help me as to how to implement the solution in Swift?
Thank!
If you want to add gradient on your imageView then you can just implement CAGradientLayer on your imageView.layer.
Try to change some values for your own custom look, but the code below is pretty much it.
let gradientLayer = CAGradientLayer()
gradientLayer.frame = imageView.frame
let colors = [
UIColor(red: 0, green: 0, blue: 0, alpha: 1).cgColor,
UIColor(red: 0, green: 0, blue: 0, alpha: 0).cgColor
]
gradientLayer.startPoint = CGPoint(x: 0.1, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 0.9, y: 0.5)
gradientLayer.colors = colors
imageView.layer.addSublayer(gradientLayer)
You can change colors, add colors, change start/end-points. You can find a lot of different CAGradientLayer-guides on youtube or google.
try this:
extension UILabel {
func lblShadow(color: UIColor , radius: CGFloat, opacity: Float){
self.textColor = color
self.layer.masksToBounds = false
self.layer.shadowRadius = radius
self.layer.shadowOpacity = opacity
self.layer.shadowOffset = CGSize(width: 1, height: 1)
self.layer.shouldRasterize = true
self.layer.rasterizationScale = UIScreen.main.scale
}
}
usage:
label.lblShadow(color: UIColor.white, radius: 3, opacity: 0.75)

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.

Applying Gradient to UIImage Smoothly

I am trying to apply gradient to UIImage using CoreGraphic; however, the result I got is not very nice. I want to create a black to transparent gradient at the bottom of the image to create a contrast for me to place some text. However, the gradient I was able doesn't blend well with the image; you can clearly see the separation in the center. The result I am looking for is like this application: http://capptivate.co/2014/02/17/yummly-3/
How should I apply the gradient to achieve this? ( I have to apply this to large quantity of images ).
My Result:
Here is my code:
func imageWithGradient(img:UIImage!) -> UIImage{
UIGraphicsBeginImageContext(img.size)
var context = UIGraphicsGetCurrentContext()
img.drawAtPoint(CGPointMake(0, 0))
let colorSpace = CGColorSpaceCreateDeviceRGB()
let locations:[CGFloat] = [0.50, 1.0]
//1 = opaque
//0 = transparent
let bottom = UIColor(red: 0, green: 0, blue: 0, alpha: 0.5).CGColor
let top = UIColor(red: 0, green: 0, blue: 0, alpha: 0).CGColor
let gradient = CGGradientCreateWithColors(colorSpace,
[top, bottom], locations)
let startPoint = CGPointMake(img.size.width/2, 0)
let endPoint = CGPointMake(img.size.width/2, img.size.height)
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
SWIFT 3
func imageWithGradient(img:UIImage!) -> UIImage {
UIGraphicsBeginImageContext(img.size)
let context = UIGraphicsGetCurrentContext()
img.draw(at: CGPoint(x: 0, y: 0))
let colorSpace = CGColorSpaceCreateDeviceRGB()
let locations:[CGFloat] = [0.0, 1.0]
let bottom = UIColor(red: 0, green: 0, blue: 0, alpha: 0.5).cgColor
let top = UIColor(red: 0, green: 0, blue: 0, alpha: 0).cgColor
let colors = [top, bottom] as CFArray
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors, locations: locations)
let startPoint = CGPoint(x: img.size.width/2, y: 0)
let endPoint = CGPoint(x: img.size.width/2, y: img.size.height)
context!.drawLinearGradient(gradient!, start: startPoint, end: endPoint, options: CGGradientDrawingOptions(rawValue: UInt32(0)))
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}
I tried this out in a Swift playground, I couldn't replicate the exact issue you are describing, however here are some suggestions that you could try.
You could change the way you set up the stops in the gradient, I think it would make more sense to instead of shifting the gradient using the stops, shift the start point, i.e instead of starting from the top edge, start from the vertical center, something like this
UIGraphicsBeginImageContext(image.size)
var context = UIGraphicsGetCurrentContext()
image.drawAtPoint(CGPointMake(0, 0))
let colorSpace = CGColorSpaceCreateDeviceRGB()
let locations:[CGFloat] = [0.0, 1.0]
let bottom = UIColor(red: 0, green: 0, blue: 0, alpha: 0.5).CGColor
let top = UIColor(red: 0, green: 0, blue: 0, alpha: 0).CGColor
let gradient = CGGradientCreateWithColors(colorSpace,
[top, bottom], locations)
let startPoint = CGPointMake(image.size.width / 2, image.size.height / 2)
let endPoint = CGPointMake(image.size.width / 2, image.size.height)
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0)
let finalImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
Notice locations is now 0 and 1, while the startPoint y coordinate has shifted downwards.
Alternatively, look at passing kCGGradientDrawsBeforeStartLocation as an option to CGContextDrawLinearGradient, as that could also make a difference.

Resources