1 // Copyright 2021-present 650 Industries. All rights reserved. 2 3 import QuartzCore 4 import CoreGraphics 5 6 var defaultStartPoint = CGPoint(x: 0.5, y: 0.0) 7 var defaultEndPoint = CGPoint(x: 0.5, y: 1.0) 8 var defaultLocations: [CGFloat] = [] 9 10 final class LinearGradientLayer: CALayer { 11 var colors = [CGColor]() 12 var startPoint = defaultStartPoint 13 var endPoint = defaultEndPoint 14 var locations = defaultLocations 15 16 override init() { 17 super.init() 18 self.needsDisplayOnBoundsChange = true 19 self.masksToBounds = true 20 } 21 22 override init(layer: Any) { 23 super.init(layer: layer) 24 self.needsDisplayOnBoundsChange = true 25 self.masksToBounds = true 26 } 27 28 required init?(coder: NSCoder) { 29 fatalError("init(coder:) has not been implemented") 30 } 31 setColorsnull32 func setColors(_ colors: [CGColor]) { 33 self.colors = colors 34 setNeedsDisplay() 35 } 36 setStartPointnull37 func setStartPoint(_ startPoint: CGPoint?) { 38 self.startPoint = startPoint ?? defaultStartPoint 39 setNeedsDisplay() 40 } 41 setEndPointnull42 func setEndPoint(_ endPoint: CGPoint?) { 43 self.endPoint = endPoint ?? defaultEndPoint 44 setNeedsDisplay() 45 } 46 setLocationsnull47 func setLocations(_ locations: [CGFloat]?) { 48 self.locations = locations ?? defaultLocations 49 setNeedsDisplay() 50 } 51 displaynull52 override func display() { 53 super.display() 54 55 if colors.isEmpty || bounds.size.width.isZero || bounds.size.height.isZero { 56 return 57 } 58 let hasAlpha = colors.reduce(false) { result, color in 59 return result || color.alpha < 1.0 60 } 61 62 UIGraphicsBeginImageContextWithOptions(bounds.size, !hasAlpha, 0.0) 63 64 guard let contextRef = UIGraphicsGetCurrentContext() else { 65 return 66 } 67 68 draw(in: contextRef) 69 70 guard let image = UIGraphicsGetImageFromCurrentImageContext() else { 71 return 72 } 73 74 self.contents = image.cgImage 75 self.contentsScale = image.scale 76 77 UIGraphicsEndImageContext() 78 } 79 drawnull80 override func draw(in ctx: CGContext) { 81 super.draw(in: ctx) 82 83 ctx.saveGState() 84 85 let colorSpace = CGColorSpaceCreateDeviceRGB() 86 let locations = colors.enumerated().map { (offset: Int, _: CGColor) -> CGFloat in 87 if self.locations.count > offset { 88 return self.locations[offset] 89 } else { 90 return CGFloat(offset) / CGFloat(colors.count - 1) 91 } 92 } 93 94 if let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: locations) { 95 let size = bounds.size 96 97 ctx.drawLinearGradient( 98 gradient, 99 start: CGPoint(x: startPoint.x * size.width, y: startPoint.y * size.height), 100 end: CGPoint(x: endPoint.x * size.width, y: endPoint.y * size.height), 101 options: [.drawsBeforeStartLocation, .drawsAfterEndLocation] 102 ) 103 } 104 105 ctx.restoreGState() 106 } 107 } 108