将CGGradient转换为CAGradientLayer

时间:2015-04-20 00:44:56

标签: ios iphone swift paint-code

我使用PaintCode StyleKit生成一堆渐变,但PaintCode将它们导出为CGGradient。我想将这些渐变添加到layer。是否可以将CGGradient转换为CAGradientLayer

3 个答案:

答案 0 :(得分:3)

没有。 CAGradientLayer的要点是你可以向它描述你想要的渐变,并为你绘制渐变。你已经过了那一步;你已经已经描述了你想要的渐变(到了CAGradientLayer的PaintCode 而不是),因此你已经拥有你想要的渐变。因此,即使想要使用CAGradientLayer也是愚蠢的,因为如果你打算这样做,为什么你首先使用PaintCode?只需将CGGradient本身绘制成图像,视图甚至图层。

答案 1 :(得分:3)

您无法从CGGradient中获取颜色,但您可以使用相同的值来设置CAGradientLayer的colorslocations属性。也许你可以修改生成的PCGradient类来保持颜色和位置,因为NSArrays可以传递给CAGradientLayer。

答案 2 :(得分:1)

如果您有一个渐变库并且偶尔需要使用两种格式之一的渐变,这可能很重要。

是的,这是可能的,但需要一些数学运算才能从常规坐标系(x值从0到宽度,y值从0到高度)转换为CAGradientLayer使用的坐标系(x值来自0到1,y值从0到1)。它需要更多的数学运算(非常复杂)才能使斜率正确。

x的0到1的距离取决于原始矩形的宽度。 y的0到1之间的距离取决于原始矩形的高度。所以:

let convertedStartX = startX/Double(width)
let convertedStartY = startY/Double(height)
let convertedEndX = endX/Double(width)
let convertedEndY = endY/Double(height)

let intermediateStartPoint = CGPoint(x:convertedStartX,y:convertedStartY)
let intermediateEndPoint = CGPoint(x:convertedEndX,y:convertedEndY)

如果您的原始矩形是正方形,则此方法有效。如果没有,定义渐变角度的线的斜率将是错误的!要解决此问题,请在此处查看优秀答案:CAGradientLayer diagonal gradient

如果从那里拿起实用程序,那么您可以按照以下方式设置最终转换的起点和终点,从上面已调整的点值开始:

let fixedStartEnd:(CGPoint,CGPoint) = LinearGradientFixer.fixPoints(start: intermediateStartPoint, end: intermediateEndPoint, bounds: CGSize(width:width,height:height))

let myGradientLayer = CAGradientLayer()
myGradientLayer.startPoint = fixedStartEnd.0
myGradientLayer.endPoint = fixedStartEnd.1

以下是完整Struct的代码,您可以使用它来存储渐变数据并根据需要返回CGGradients或CAGradientLayers:

import UIKit

struct UniversalGradient {

    //Doubles are more precise than CGFloats for the
    //calculations needed to convert start and end
    //to CAGradientLayers 1...0 format

    var startX: Double
    var startY: Double
    var endX: Double
    var endY: Double

    let options: CGGradientDrawingOptions = [.drawsBeforeStartLocation, .drawsAfterEndLocation]

    //for CAGradientLayer
    var colors: [UIColor]
    var locations: [Double]

    //computed conversions
    private var myCGColors: [CGColor] {
        return self.colors .map {color in color.cgColor}
    }

    private var myCGFloatLocations: [CGFloat] {
        return self.locations .map {location in CGFloat(location)}
    }

    //computed properties
    var gradient: CGGradient {
        return CGGradient(colorsSpace: nil, colors: myCGColors as CFArray, locations: myCGFloatLocations)!
    }

    var start: CGPoint {
        return CGPoint(x: startX,y: startY)
    }
    var end: CGPoint {
        return CGPoint(x: endX,y: endY)
    }

    //can't use computed property here
    //since we need details of the specific environment's bounds to be passed in
    //so this will be an instance function

    func gradientLayer(width: CGFloat, height: CGFloat) -> CAGradientLayer {

        //convert location x and y values from full coordinates to 0...1 for start and end
        //this works great for position, but it gets the slope incorrect if the view is not square

        //this is because the gradient is not drawn with the final scale
        //it is drawn while the view is square, and then it gets stretched, changing the angle
        //https://stackoverflow.com/questions/38821631/cagradientlayer-diagonal-gradient

        let convertedStartX = startX/Double(width)
        let convertedStartY = startY/Double(height)
        let convertedEndX = endX/Double(width)
        let convertedEndY = endY/Double(height)

        let intermediateStartPoint = CGPoint(x:convertedStartX,y:convertedStartY)
        let intermediateEndPoint = CGPoint(x:convertedEndX,y:convertedEndY)

        let fixedStartEnd:(CGPoint,CGPoint) = LinearGradientFixer.fixPoints(start: intermediateStartPoint, end: intermediateEndPoint, bounds: CGSize(width:width,height:height))

        let myGradientLayer = CAGradientLayer()
        myGradientLayer.startPoint = fixedStartEnd.0
        myGradientLayer.endPoint = fixedStartEnd.1

        myGradientLayer.locations = self.locations .map {location in NSNumber(value: location)}
        myGradientLayer.colors = self.colors .map {color in color.cgColor}

        return myGradientLayer
    }

}