Skip to content

Instantly share code, notes, and snippets.

@LiLejia
Created September 27, 2019 06:06
Show Gist options
  • Save LiLejia/e743a5b11c8ecf738489d81452851a22 to your computer and use it in GitHub Desktop.
Save LiLejia/e743a5b11c8ecf738489d81452851a22 to your computer and use it in GitHub Desktop.
Photoshop style gradients in Core Graphics via use of CGShader (with thanks to @jernejstrasner, see his explanation of why your gradients might not look how your designers intended http://jernejstrasner.com/2014/01/09/smooth-gradients-ios.html)
import UIKit
func SineEaseInOutLinearAverage(x: CGFloat) -> CGFloat {
let easeInOutSine = ((cos(CGFloat.pi * x) - 1) / -2)
return (easeInOutSine + x) / 2
}
func ShadingFunctionCreate(_ startColor: UIColor, _ endColor: UIColor, _ slopeFunction: @escaping (CGFloat) -> CGFloat) -> (UnsafePointer<CGFloat>, UnsafeMutablePointer<CGFloat>) -> Void {
return { inData, outData in
let q = slopeFunction(inData[0])
let startComponents = startColor.cgColor.components!
let endCOmponets = endColor.cgColor.components!
outData[0] = startComponents[0] + (endCOmponets[0] - startComponents[0]) * q
outData[1] = startComponents[1] + (endCOmponets[1] - startComponents[1]) * q
outData[2] = startComponents[2] + (endCOmponets[2] - startComponents[2]) * q
outData[3] = startComponents[3] + (endCOmponets[3] - startComponents[3]) * q
}
}
func ShadingCallback(infoPointer: UnsafeMutableRawPointer?, inData: UnsafePointer<CGFloat>, outData: UnsafeMutablePointer<CGFloat>) -> Void {
if var info = infoPointer?.load(as: Gradient.self) {
info.shadingFunction(inData, outData)
}
}
struct Gradient {
let startColor : UIColor
let endColor : UIColor
let from : CGPoint
let to : CGPoint
let colorSpace = CGColorSpaceCreateDeviceRGB()
init(startColor: UIColor, endColor: UIColor, from: CGPoint, to: CGPoint) {
self.startColor = startColor
self.endColor = endColor
self.from = from
self.to = to
}
lazy var shadingFunction : (UnsafePointer<CGFloat>, UnsafeMutablePointer<CGFloat>) -> Void = {
return ShadingFunctionCreate(self.startColor, self.endColor, SineEaseInOutLinearAverage)
}()
lazy var cgfunction : CGFunction? = {
var callbacks = CGFunctionCallbacks(version: 0, evaluate: ShadingCallback, releaseInfo: nil)
let byteCount = MemoryLayout<Gradient>.size
let alignment = MemoryLayout<Gradient>.alignment
let infoPointer = UnsafeMutableRawPointer.allocate(byteCount: byteCount, alignment: alignment)
infoPointer.storeBytes(of: self, as: Gradient.self)
defer{
infoPointer.deallocate()
}
return CGFunction(info: infoPointer, domainDimension: 1, domain: [0, 1], rangeDimension: 4, range: [0, 1, 0, 1, 0, 1, 0, 1], callbacks: &callbacks)
}()
lazy var cgShading : CGShading! = CGShading(axialSpace: self.colorSpace, start: self.from, end: self.to, function: self.cgfunction!, extendStart: false, extendEnd: false)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment