I can demonstrate the issue with this simple example.
I have a view which needs diagonal gradient which should start with one color at top left corner and change to another color at the bottom right corner. The following code shows my attempt:
import UIKit
class GradientView: UIView {
override func draw(_ rect: CGRect) {
let context = UIGraphicsGetCurrentContext()!
context.saveGState()
context.clip(to: rect)
let topColor = UIColor.red
let bottomColor = UIColor.green
var tr = CGFloat(0), tg = CGFloat(0), tb = CGFloat(0), ta = CGFloat(0), br = CGFloat(0), bg = CGFloat(0), bb = CGFloat(0), ba = CGFloat(0)//top and bottom component variables
topColor.getRed(&tr, green: &tg, blue: &tb, alpha: &ta)
bottomColor.getRed(&br, green: &bg, blue: &bb, alpha: &ba)
let gradient = CGGradient(colorSpace: CGColorSpaceCreateDeviceRGB(), colorComponents: [tr,tg,tb,ta,br,bg,bb,ba], locations: [0.0, 1.0], count: 2)
context.drawLinearGradient(gradient!, start: CGPoint(x: 0, y: 0), end: CGPoint(x: rect.size.width, y: rect.size.height), options: CGGradientDrawingOptions.drawsAfterEndLocation)
context.restoreGState()
}
}
Here’s how it looks:
As you can see, in the bottom square, when height is same as width, it works fine. However, in the top rectangle, when the width is longer than height, it doesn’t work correctly as the top right corner doesn’t have any red whatsoever and the bottom left corner doesn’t have any green whatsoever.
How can I fix this?
2
Answers
Assuming the layer is masking to bounds, you could use the max value between the width and the height as the reference value when creating the gradient.
Meaning, always create a square gradient, whose size is the max between those values.
This will make the gradient render at a 45 degree always, as opposed to what you are experiencing. The issue is not that the gradient is not drawing properly, but rather the slope is different because of the size.
In order to get the desired result you need to calculate start and end points that connect a line that is perpendicular to the diagonal line through the rectangle. See the following diagram:
The following update to your code calculates the values for dx and dy which can be used to get the start and end points of the gradient.
The following sample views (wide, tall, and square) demonstrate the desired output for all three cases:
The results are:
wgv:
tgv:
and sgv: