Radial gradient uses unexpected gray color - ios

TL;DR
Does anyone know how I can smooth out the gradient transition from red to white in a radial gradient? Because I'm getting an ugly gray color in my gradient.
Details:
I created a custom UIView with the following in draw rect
override func draw(_ rect: CGRect) {
let colors = [UIColor.red.cgColor, UIColor.clear.cgColor] as CFArray
let divisor: CGFloat = 3
let endRadius = sqrt(pow(frame.width/divisor, 2) + pow(frame.height/divisor, 2))
let center = CGPoint(x: bounds.midX, y: bounds.midY)
let gradient = CGGradient(colorsSpace: nil, colors: colors, locations: nil)
let context = UIGraphicsGetCurrentContext()
context?.saveGState()
context?.drawRadialGradient(gradient!,
startCenter: center,
startRadius: 0.0,
endCenter: center,
endRadius: endRadius,
options: .drawsBeforeStartLocation)
context?.restoreGState()
}
I set the custom view's color to red and added the view to the viewcontroller's view, which has a white background.
I wanted the gradient to go from red to white but it seems to go from red to gray. I tried changing the second color in the gradient array to white, and though it did look slightly better, the grayness still remains.

When using clear color on transparent gradients keep in mind that the clear color is generated by applying the black color an alpha and thats why you get the greyish color on your case.
To solve this in your code change UIColor.clear.cgColor, from colors array, with UIColor.white.withAlphaComponent(0.0).cgColor. This way you can get the gradient you expect.

Related

Apply Beveled Radial Gradient to UIView

I'm currently using the following code here
#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)
}
}
This code will draw a radial gradient that looks like this:
What I am trying to do is make it look like below (which is how it looks in the current desktop version of the app).
You will notice that in the desktop version the gradient has blue at the top and bottom and the white area is stretched to include more of the text area so it's easier to read.
How can I recreate this effect? I have tried for hours playing with different numbers and settings in the current code to no avail so far (such as changing the endRadius).
Any help or ideas are appreciated.

Gradient Transparent view swift

im trying to make a gradient transparent view alert as shown in the picture below, any help ?
#IBInspectable var InsideColor: UIColor = UIColor.clear
#IBInspectable var OutsideColor: UIColor = UIColor.clear
override func draw(_ rect: CGRect) {
let colors = [InsideColor.cgColor, OutsideColor.cgColor] as CFArray
let endRadius = min(frame.width, frame.height) / 2
let center = CGPoint(x: bounds.size.width / 2, y: bounds.size.height / 2)
let gradient = CGGradient(colorsSpace: nil, colors: colors, locations: nil)
UIGraphicsGetCurrentContext()!.drawRadialGradient(gradient!, startCenter: center, startRadius: 0.0, endCenter: center, endRadius: endRadius, options: CGGradientDrawingOptions.drawsBeforeStartLocation)
}
It is better to have it statically in your project assets instead of creating it dynamically every time. So:
Create an image with the clear center and black fill color like this:
Note that the center part is NOT white. It's transparent. So you should see behind it like this example:
Then add an imageView and fill it with this image.
Set the backgroundColor of the image view to .clear
done.
Note: Don't forget to set the backgroundColor of the original view of your custom alert to .clear and modalPresentationStyle of the custom alert controller to .overCurrentContext

Drawing A Circle With Gradient Stroke

I am working on a task in which I must create a circle with no fill, but a gradient stroke. For reference, here is the end result I am after;
Given other occurrences with the app, I am drawing my circle like so;
let c = UIGraphicsGetCurrentContext()!
c.saveGState()
let clipPath: CGPath = UIBezierPath(roundedRect: converted_rect, cornerRadius: converted_rect.width / 2).cgPath
c.addPath(clipPath)
c.setLineWidth(9.0)
c.setStrokeColor(UIColor.blue.cgColor)
c.closePath()
c.strokePath()
c.restoreGState()
let result = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
This results in a circle with a blue stroke. Despite many searches around SO, I'm struggling to figure out how I'd replace that setStrokeColor with a gradient, rather than a blue color. My most success came from creating a CAGradientLayer, then masking it with a CAShapeLayer created from the path, but I was only able to create a filled circle, not a hollow circle.
Thank you!
The basic idea is to use your path as a clipping path, then draw the gradient.
let c = UIGraphicsGetCurrentContext()!
let clipPath: CGPath = UIBezierPath(ovalIn: converted_rect).cgPath
c.saveGState()
c.setLineWidth(9.0)
c.addPath(clipPath)
c.replacePathWithStrokedPath()
c.clip()
// Draw gradient
let colors = [UIColor.blue.cgColor, UIColor.red.cgColor]
let offsets = [ CGFloat(0.0), CGFloat(1.0) ]
let grad = CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(), colors: colors as CFArray, locations: offsets)
let start = converted_rect.origin
let end = CGPoint(x: converted_rect.maxX, y: converted_rect.maxY)
c.drawLinearGradient(grad!, start: start, end: end, options: [])
c.restoreGState()
let result = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
Setup a CGGradient first with the desired colors. Then for a linear gradient you use drawLinearGradient. For a radial gradient, use drawRadialGradient.

Add gradient color on border of View

I am using swift 2.2 .I have a view and i want to use gradient color on inner border sides of view.Please help me.I am using the following code.Here i attach the screenshot which i need view.
self.layer.borderWidth = 1
self.layer.borderColor = UIColor(white:0.1, alpha: 0.5).CGColor
try this code
#IBOutlet weak var gradientView: UIView!
gradientView.backgroundColor = UIColor.clearColor()
let gradient:CAGradientLayer = CAGradientLayer()
gradient.startPoint = CGPoint(x: 0.0, y: 1.0)
gradient.endPoint = CGPoint(x: 0.0, y: 0.0)
gradient.frame.size = self.gradientView.frame.size
gradient.colors = [UIColor.darkGrayColor().CGColor,UIColor.darkGrayColor().colorWithAlphaComponent(0).CGColor] //Or any colors
gradient.locations = [ 0.0, 1.0]
self.gradientView.layer.insertSublayer(gradient, atIndex: 0)
iPatel's answer would work, but if you want to use the gradient function rather than an image, you could simply place a view with a gradient fill behind a slightly smaller view that holds your content. As long as the smaller, front view is inset from the gradient view by the same distance on all sides then this will give the effect that you are looking for.
As per my suggestion use below code for achieve gradient color
self.layer.borderColor = UIColor(patternImage:UIImage(named: "myCorrectImgName.png")!).CGColor
Where "myCorrectImgName.png" is name of image that have gradient color or required image that you want.
Otherwise you will be find may examples from the Google for how to set gradient color.

Fastest way to draw a full-screen linear gradient in iOS

I need to draw an animated linear gradient that is updated on every frame, using CADisplayLink. Drawing directly into a full-screen context with CoreGraphics is slow, I get around 40fps at full CPU load on an iPad Air.
What's the fastest way to do this?
Update
As #Kurt Revis pointed out in a comment on the question, the proper - and fastest - way to do this is to use CAGradientLayer. Add a CAGradientLayer to your view that fills its bounds, then:
func updateGradient() {
// Sample random gradient
let gradientLocs:[CGFloat] = [0.0, 0.5, 1.0]
let gradientColors:[CGColorRef] = [
UIColor.whiteColor().CGColor,
UIColor.init(colorLiteralRed: 0.0, green: 0.0, blue: 0.25*Float(arc4random())/Float(UINT32_MAX), alpha: 1.0).CGColor,
UIColor.blackColor().CGColor
]
// Disable implicit animations if this is called via CADisplayLink
CATransaction.begin()
CATransaction.setDisableActions(true)
// Draw to existing CAGradientLayer
gradientBackgroundLayer.colors = gradientColors
gradientBackgroundLayer.locations = gradientLocs
CATransaction.commit()
}
Old answer
After some experimentation, I ended up using CoreGraphics to draw the gradient into a 1px wide CGImage and then rely on UIImageView to do the scaling. I get solid 60fps at about 7-11% CPU load, depending on how many colors the gradient contains:
Subclass UIImageView with contentMode set to ScaleToFill, then call the following via CADisplayLink for a continuous animation.
func updateGradient() {
// Sample random gradient
let gradientLocs:[CGFloat] = [0.0, 0.5, 1.0]
let gradientColors:[CGColorRef] = [
UIColor.whiteColor().CGColor,
UIColor.init(colorLiteralRed: 0.0, green: 0.0, blue: 0.25*Float(arc4random())/Float(UINT32_MAX), alpha: 1.0).CGColor,
UIColor.blackColor().CGColor
]
// Create context at 1px width and display height
let gradientWidth:Double = 1
let gradientHeight:Double = Double(UIScreen.mainScreen().bounds.height)
UIGraphicsBeginImageContext(CGSize(width: gradientWidth, height: gradientHeight))
let buffer:CGContextRef = UIGraphicsGetCurrentContext()!
// Draw gradient into buffer
let colorspace = CGColorSpaceCreateDeviceRGB()
let gradient = CGGradientCreateWithColors(colorspace, gradientColors, gradientLocs)
CGContextDrawLinearGradient(
buffer,
gradient,
CGPoint(x: 0.0, y: 0.0),
CGPoint(x: 0.0, y: gradientHeight),
CGGradientDrawingOptions.DrawsAfterEndLocation
)
// Let UIImageView superclass handle scaling
image = UIImage.init(CGImage: CGBitmapContextCreateImage(buffer)!)
// Avoid memory leaks...
UIGraphicsEndImageContext()
}
Caveat: this approach will only work for horizontal or vertical linear gradients.
Are there better ways of doing this?

Resources