108 lines
2.7 KiB
Swift
108 lines
2.7 KiB
Swift
// Copyright 2021-present 650 Industries. All rights reserved.
|
|
|
|
import QuartzCore
|
|
import CoreGraphics
|
|
|
|
var defaultStartPoint = CGPoint(x: 0.5, y: 0.0)
|
|
var defaultEndPoint = CGPoint(x: 0.5, y: 1.0)
|
|
var defaultLocations: [CGFloat] = []
|
|
|
|
final class LinearGradientLayer: CALayer {
|
|
var colors = [CGColor]()
|
|
var startPoint = defaultStartPoint
|
|
var endPoint = defaultEndPoint
|
|
var locations = defaultLocations
|
|
|
|
override init() {
|
|
super.init()
|
|
self.needsDisplayOnBoundsChange = true
|
|
self.masksToBounds = true
|
|
}
|
|
|
|
override init(layer: Any) {
|
|
super.init(layer: layer)
|
|
self.needsDisplayOnBoundsChange = true
|
|
self.masksToBounds = true
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
func setColors(_ colors: [CGColor]) {
|
|
self.colors = colors
|
|
setNeedsDisplay()
|
|
}
|
|
|
|
func setStartPoint(_ startPoint: CGPoint?) {
|
|
self.startPoint = startPoint ?? defaultStartPoint
|
|
setNeedsDisplay()
|
|
}
|
|
|
|
func setEndPoint(_ endPoint: CGPoint?) {
|
|
self.endPoint = endPoint ?? defaultEndPoint
|
|
setNeedsDisplay()
|
|
}
|
|
|
|
func setLocations(_ locations: [CGFloat]?) {
|
|
self.locations = locations ?? defaultLocations
|
|
setNeedsDisplay()
|
|
}
|
|
|
|
override func display() {
|
|
super.display()
|
|
|
|
if colors.isEmpty || bounds.size.width.isZero || bounds.size.height.isZero {
|
|
return
|
|
}
|
|
let hasAlpha = colors.reduce(false) { result, color in
|
|
return result || color.alpha < 1.0
|
|
}
|
|
|
|
UIGraphicsBeginImageContextWithOptions(bounds.size, !hasAlpha, 0.0)
|
|
|
|
guard let contextRef = UIGraphicsGetCurrentContext() else {
|
|
return
|
|
}
|
|
|
|
draw(in: contextRef)
|
|
|
|
guard let image = UIGraphicsGetImageFromCurrentImageContext() else {
|
|
return
|
|
}
|
|
|
|
self.contents = image.cgImage
|
|
self.contentsScale = image.scale
|
|
|
|
UIGraphicsEndImageContext()
|
|
}
|
|
|
|
override func draw(in ctx: CGContext) {
|
|
super.draw(in: ctx)
|
|
|
|
ctx.saveGState()
|
|
|
|
let colorSpace = CGColorSpaceCreateDeviceRGB()
|
|
let locations = colors.enumerated().map { (offset: Int, _: CGColor) -> CGFloat in
|
|
if self.locations.count > offset {
|
|
return self.locations[offset]
|
|
} else {
|
|
return CGFloat(offset) / CGFloat(colors.count - 1)
|
|
}
|
|
}
|
|
|
|
if let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: locations) {
|
|
let size = bounds.size
|
|
|
|
ctx.drawLinearGradient(
|
|
gradient,
|
|
start: CGPoint(x: startPoint.x * size.width, y: startPoint.y * size.height),
|
|
end: CGPoint(x: endPoint.x * size.width, y: endPoint.y * size.height),
|
|
options: [.drawsBeforeStartLocation, .drawsAfterEndLocation]
|
|
)
|
|
}
|
|
|
|
ctx.restoreGState()
|
|
}
|
|
}
|