【问题标题】:Right way to reproduce sketch gradient with CAGradientLayer使用 CAGradientLayer 重现草图渐变的正确方法
【发布时间】:2018-03-21 00:58:37
【问题描述】:

我想创建一个具有四种给定颜色的渐变层。我有一个渐变的草图文件。我有色标的坐标。我计算了 CAGradientLayer 起点和终点的归一化坐标。我还计算了颜色相互变化的位置。图层仍然与草图图像不匹配。你能帮我看看有什么问题吗?导出图像的使用不是一个选项,因为图层必须随着动画变化到其他渐变位置。

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)
    }
}

我附上了一些屏幕截图以明确区别。差别不大,但仍然如此。

【问题讨论】:

标签: ios swift calayer cagradientlayer


【解决方案1】:

Sketch 渲染图形的方式与 iOS 不同。即使您完美匹配颜色和位置,最终结果仍然会有所不同。

如果您希望与 Sketch 文件完全匹配,则需要对颜色进行一些光学调整以解决差异。你的两张图片非常接近,所以通过一些小的颜色调整,它应该看起来更接近了。

试着让你的浅蓝色(左上角)和浅紫色(右下角)不那么鲜艳。我无权访问您的 Sketch 文件,因此我无法建议新的颜色来获得完全匹配,但这应该不会太难,只是一些尝试和错误。

【讨论】:

  • 如何进行这些调整?只是做实验?应该以其他方式存在
  • @Gergely 是的,只要尝试一些新颜色,直到它看起来不错。我将从当前感兴趣的颜色开始(比如左上角的浅蓝色),然后进入 Sketch 并使颜色稍微不那么鲜艳(在颜色选择器框中将颜色向下和向左移动)。看看它是否有效,然后从那里进行调整。在这一点上,选择颜色是一门艺术而不是一门科学。
  • 具有讽刺意味的是,我无法做到这一点:D 当我手动进行任何更改时,情况会变得更糟..
  • @Gergely 更改将非常小。如果在 Sketch 颜色选择器上移动颜色不起作用,请尝试从原始颜色开始,并使用下面的彩虹滑块来获得稍暗的色调。同样,这些都是非常小的调整,因此您不需要过多地移动颜色。
【解决方案2】:

我写了一个脚本将 Sketch 渐变层转换为CALayer。通过view.layer.insertSublayer(gradientLayer, at: 0)插入此层。

链接:Convert Sketch Gradient To Swift Code

选择一个包含一个渐变填充的图层,然后按control + shift + K,粘贴下面的代码并运行。你会看到:

在 iPhone 上它看起来像:

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;
        }
      }
    );

  });
}


【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多