Reputation: 448
I would like to create a gradient layer with four given colours. I have a sketch file for the gradients. I have the coordinates for the colour stops. I calculated the normalised coordinates for the start and the end point for the CAGradientLayer. I also calculated the location where the colours change each other. Still the layer does not match with the sketch image. Can you help me what could be wrong? Usage of the exported image is not an option because the layer has to change with animation to other gradient locations.
let imageWidth: CGFloat = 375.0
let imageHeihgt: CGFloat = 293.0
class ViewController: UIViewController {
let homeColors: [UIColor] = [UIColor(red: 62/255, green: 131/255, blue: 255/255, alpha: 1.0),
UIColor(red: 99/255, green: 22/255, blue: 203/255, alpha: 1.0),
UIColor(red: 122/255, green: 5/255, blue: 239/255, alpha: 1.0),
UIColor(red: 122/255, green: 5/255, blue: 240/255, alpha: 1.0)]
let startPoint: CGPoint = CGPoint(x: -0.225/imageWidth*UIScreen.main.bounds.width, y: -0.582*imageHeihgt/UIScreen.main.bounds.height)
let endPoint: CGPoint = CGPoint(x: 1.088/imageWidth*UIScreen.main.bounds.width, y: 1.01*imageHeihgt/UIScreen.main.bounds.height)
let locations: [NSNumber] = [0.0, 0.734, 0.962, 1.0]
var subview: UIView!
override func viewDidLoad() {
super.viewDidLoad()
subview = UIView(frame: CGRect(x: 0, y: 20, width: imageWidth, height: imageHeihgt))
view.addSubview(subview)
view.sendSubview(toBack: subview)
addGradientLayer()
}
func addGradientLayer() {
let gradient = CAGradientLayer()
gradient.frame = subview.bounds
gradient.colors = homeColors.map { $0.cgColor }
gradient.startPoint = startPoint
gradient.endPoint = endPoint
gradient.locations = locations
subview.layer.insertSublayer(gradient, at: 0)
}
}
I have attached some screenshots to make the difference clear. It is not a big difference but still.
Upvotes: 7
Views: 1506
Reputation: 330
I wrote a script convert Sketch gradient layer to CALayer
. Insert this layer by view.layer.insertSublayer(gradientLayer, at: 0)
.
Link: Convert Sketch Gradient To Swift Code
Select a layer which contains one gradient fill, then press control + shift + K
, paste code below and run. you will see:
And on iPhone it looks like:
console.log('Select Layer Then Run Script.');
var sketch = require('sketch');
var UI = require('sketch/ui');
var document = sketch.getSelectedDocument();
var selectedLayers = document.selectedLayers;
var selectedCount = selectedLayers.length;
function hexToRGBA(hex) {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16),
a: parseInt(result[4], 16)
} : null;
}
let precesion = 10000
function parseGradient(layerGradient) {
let colorItems = ""
let locationArray = ""
layerGradient.stops.forEach(elm => {
let rgbaColor = hexToRGBA(elm.color)
colorItems += `UIColor(red: ${rgbaColor.r}/255.0, green: ${rgbaColor.g}/255.0, blue: ${rgbaColor.b}/255.0, alpha: ${rgbaColor.a}/255.0), `
locationArray += `${Math.round(elm.position*precesion)/precesion}, `
});
var codeTemplate = `
import Foundation
import UIKit
class LinearGradientLayer: CALayer {
required override init() {
super.init()
needsDisplayOnBoundsChange = true
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
required override init(layer: Any) {
super.init(layer: layer)
}
let colors = [${colorItems}].map(\{ \$0.cgColor \})
override func draw(in ctx: CGContext) {
ctx.saveGState()
let colorSpace = CGColorSpaceCreateDeviceRGB()
let locations: [CGFloat] = [${locationArray}]
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: locations)!
ctx.drawLinearGradient(gradient, start: CGPoint(x: ${Math.round(layerGradient.from.x*precesion)/precesion}*bounds.width, y: ${Math.round(layerGradient.from.y*precesion)/precesion}*bounds.height), end: CGPoint(x: ${Math.round(layerGradient.to.x*precesion)/precesion}*bounds.width, y: ${Math.round(layerGradient.to.y*precesion)/precesion}*bounds.height), options: [.drawsBeforeStartLocation, .drawsAfterEndLocation])
}
}
`;
return codeTemplate
}
if (selectedCount === 0) {
UI.alert('No layers are selected', 'Select one gradient filled layer and rerun.')
} else {
selectedLayers.forEach(function (layer, i) {
console.log((i + 1) + '. ' + layer.name);
let layerGradient = layer.style.fills[0].gradient;
console.log(layerGradient)
UI.getInputFromUser(
"Convert Result",
{
initialValue: parseGradient(layerGradient),
numberOfLines: 12
},
(err, value) => {
if (err) {
// most likely the user canceled the input
return;
}
}
);
});
}
Upvotes: 0
Reputation: 9777
Sketch renders graphics differently than iOS. Even if you match the colors and locations perfectly, the end result will still look different.
If you want an exact match to the Sketch file, you'll need to make some optical adjustments to the colors to account for the differences. Your two images are very close, so with some minor color adjustments, it should look a lot closer.
Try making your light blue (top left) and light purple (bottom right) less vibrant. I don't have access to your Sketch file, so I can't suggest new colors to get an exact match, but it shouldn't be too hard, just a little trial and error.
Upvotes: 0