Reputation: 3196
Objective
I am trying to draw a linear gradient on top of an image.
Setup
I have an function call imageWithGradient
which takes the colors and the imageView to put the image with gradient drawn on in. I added the imageView to the view using storyboard.
Problem
When I try the code on the simulator, sometimes the gradient wouldn't appear, sometimes the alpha wouldn't appear (solid color), sometimes the gradient would be flipped. Since I am running the same code with the same color it should apply the same gradient to all the images, but it rarely does.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var c1 = UIColor(red: 1.0, green: 0, blue: 0, alpha: 0.9).CGColor
var c2 = UIColor(red: 0, green: 0, blue: 0, alpha: 0).CGColor
imageWithGradient(self.imageView1, startColor: c1, endColor: c2)
imageWithGradient(self.imageView2, startColor: c1, endColor: c2)
}
func imageWithGradient(imgView:UIImageView, startColor: CGColor, endColor: CGColor){
var img = UIImage(named:"sample.jpg")!
UIGraphicsBeginImageContext(img.size)
var context = UIGraphicsGetCurrentContext()
img.drawAtPoint(CGPointMake(0, 0))
var colorSpace = CGColorSpaceCreateDeviceRGB()
var startColorComp = CGColorGetComponents(startColor)
var endColorComp = CGColorGetComponents(endColor)
var colorComps = [startColorComp[0], endColorComp[0]]
var locations:[CGFloat] = [0.0, 1.0]
var gradient = CGGradientCreateWithColorComponents(colorSpace, &colorComps, &locations, 2)
var startPoint = CGPointMake(img.size.width/2, img.size.height/2)
var endPoint = CGPointMake(img.size.width/2, img.size.height)
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0)
imgView.image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
Result
Upvotes: 0
Views: 688
Reputation: 41226
You are essentially passing in an uninitialized block of memory for the color components.
var colorComps = [startColorComp[0], endColorComp[0]]
creates an array consisting of the first component of each color, so 2 components (not even the components you want)
Further on, you pass that array of 2 elements in for:
let gradient = CGGradientCreateWithColorComponents(colorSpace, colorComps, locations, 2)
where colorComps expects to be receiving an array of 8 floats, so the other 6 floats are basically random memory contents, hence the results vary widely.
You can use this function to extract the color components into an array of the actual color components. This builds a swift array of the 4 components of the color (or however many there were, but it should be 4)
func CGColorComponents(color:CGColorRef) -> [CGFloat] {
let count = CGColorGetNumberOfComponents(color)
var comps = CGColorGetComponents(color)
var array = [CGFloat]()
for i in 0 ..< count {
array.append(comps.memory)
comps = comps.successor()
}
return array
}
You can build the appropriate array of components using:
let startColorComp = CGColorComponents(startColor)
let endColorComp = CGColorComponents(endColor)
let colorComps = startColorComp + endColorComp
Put it all together and you can create a function that'll overlay the gradient on your image:
func imageWithGradient(startColor: CGColor, endColor: CGColor) -> UIImage {
var img = UIImage(named:"img_0002.png")!
UIGraphicsBeginImageContext(img.size)
var context = UIGraphicsGetCurrentContext()
img.drawAtPoint(CGPointMake(0, 0))
let colorSpace = CGColorSpaceCreateDeviceRGB()
let componentCount = CGColorSpaceGetNumberOfComponents(colorSpace)
let startColorComp = CGColorComponents(startColor)
let endColorComp = CGColorComponents(endColor)
let colorComps = startColorComp + endColorComp
let locations:[CGFloat] = [0.0, 1.0]
let gradient = CGGradientCreateWithColorComponents(colorSpace, colorComps, locations, 2)
let startPoint = CGPointMake(img.size.width/2, img.size.height/2)
let endPoint = CGPointMake(img.size.width/2, img.size.height)
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
Not quite how you were using it, but you should be able to dig the functionality out if you really want to maintain your semantics.
Better yet, just use CGGradientCreateWithColors
instead:
let gradient = CGGradientCreateWithColors(colorSpace, [startColor, endColor], locations)
Upvotes: 3