【问题标题】:Converting a CGGradient to a CAGradientLayer将 CGGradient 转换为 CAGradientLayer
【发布时间】:2015-04-20 00:44:55
【问题描述】:

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

【问题讨论】:

  • 您是否出于某种特定原因需要对其进行转换?您可以在 UIView 中绘制 CGGradient。
  • 更正:PaintCode 生成 PCGradient 包装 CGGradientRef 的类。

标签: ios iphone swift paintcode


【解决方案1】:

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

【讨论】:

  • 部分正确,但是当您在 PaintCode 中创建整个应用程序并且您只想在 CAShapeLayer 中使用一个渐变(用于动画或其他内容)时,能够获取颜色和位置分开。我们会看看这个。
【解决方案2】:

您无法从 CGGradient 中获取颜色,但您可以使用相同的值来设置 CAGradientLayer 的 colorslocations 属性。也许修改生成的PCGradient 类对您有所帮助,以将颜色和位置保留为可以传递给 CAGradientLayer 的 NSArray。

【讨论】:

    【解决方案3】:

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

    是的,这是可能的,但需要一些数学才能从常规坐标系(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
    

    这里是完整结构的代码,您可以使用它来存储梯度数据并根据需要取回 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
        }
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-06-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-13
      • 2011-02-03
      • 1970-01-01
      相关资源
      最近更新 更多