简体   繁体   中英

How to Add a CAGradientLayer to a CAShapeLayer?

I am trying to add a CAGradientLayer to a CAShapeLayer, but the gradient is not conforming to the shape of the layer. Instead, the gradient is taking the shape of the UIView itself. I originally attempted this with a UIBezierPath but it did not work either. I am trying to put a gradient into my shape.

  override func drawRect(rect: CGRect) {
        let bounds = CGRect(x: 0, y: 0, width: self.bounds.width, height: self.bounds.height)
        let center = self.center

        let path = CAShapeLayer()
        path.bounds = bounds
        path.position = CGPoint(x: center.x, y: center.y)
        //path.backgroundColor = UIColor.redColor().CGColor
        path.cornerRadius = 20
        //self.layer.addSublayer(path)
        let gradientLayer = CAGradientLayer()
        gradientLayer.frame = path.bounds
        gradientLayer.colors = [cgColorForRed(209.0, green: 0.0, blue: 0.0),
            cgColorForRed(255.0, green: 102.0, blue: 34.0),
            cgColorForRed(255.0, green: 218.0, blue: 33.0),
            cgColorForRed(51.0, green: 221.0, blue: 0.0),
            cgColorForRed(17.0, green: 51.0, blue: 204.0),
            cgColorForRed(34.0, green: 0.0, blue: 102.0),
            cgColorForRed(51.0, green: 0.0, blue: 68.0)]
        gradientLayer.startPoint = CGPoint(x:0,y:0)
        gradientLayer.endPoint = CGPoint(x:0, y:1)
        path.insertSublayer(gradientLayer, atIndex: 1)
        self.layer.addSublayer(path)


    }

    func cgColorForRed(red: CGFloat, green: CGFloat, blue: CGFloat ) -> AnyObject {
        return UIColor(red: red/255.0, green: green/255.0, blue: blue/255.5, alpha: 1.0).CGColor as AnyObject
    }

Originally I had path defined to be a UIBezierPath of a rectangle with rounded corners which I am looking to fill with the gradient:

        let insetRect = CGRectInset(rect, lineWidth / 2, lineWidth / 2)

        let path = UIBezierPath(roundedRect: insetRect, cornerRadius: 10)

        fillColor.setFill()
        path.fill()

        path.lineWidth = self.lineWidth
        UIColor.blackColor().setStroke()
        path.stroke()

Edit from Larcerax's response:

class Thermometer: UIView {

    //@IBInspectable var fillColor: UIColor = UIColor.greenColor()
    let lineWidth: CGFloat = 2

    override func drawRect(rect: CGRect) {

        let context = UIGraphicsGetCurrentContext()
        let svgid = CGGradientCreateWithColors(CGColorSpaceCreateDeviceRGB(), [cgColorForRed(209.0, green: 0.0, blue: 0.0),
            cgColorForRed(255.0, green: 102.0, blue: 34.0),
            cgColorForRed(255.0, green: 218.0, blue: 33.0),
            cgColorForRed(51.0, green: 221.0, blue: 0.0),
            cgColorForRed(17.0, green: 51.0, blue: 204.0),
            cgColorForRed(34.0, green: 0.0, blue: 102.0),
            cgColorForRed(51.0, green: 0.0, blue: 68.0)], [0,1])
        CGContextSaveGState(context)
        let insetRect = CGRectInset(rect, lineWidth / 2, lineWidth / 2)

        let path = UIBezierPath(roundedRect: insetRect, cornerRadius: 10)
        path.addClip()
        CGContextDrawLinearGradient(context, svgid, CGPoint(x: 0, y: path.bounds.height),
            CGPoint(x: path.bounds.width, y: path.bounds.height),
            UInt32(kCGGradientDrawsBeforeStartLocation) | UInt32(kCGGradientDrawsAfterEndLocation))
        CGContextRestoreGState(context)
}

You can try something like the following set up in order to produce the following shape, this is just an example to show you the setup, but this should do it:

    let context = UIGraphicsGetCurrentContext()

    let gradientColor3 = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)
    let gradientColor4 = UIColor(red: 0.000, green: 1.000, blue: 0.000, alpha: 1.000)

    let sVGID_1_2 = CGGradientCreateWithColors(CGColorSpaceCreateDeviceRGB(), [gradientColor3.CGColor, gradientColor4.CGColor], [0, 1])

    CGContextSaveGState(context)
    CGContextTranslateCTM(context, 198.95, 199.4)
    CGContextRotateCTM(context, -30 * CGFloat(M_PI) / 180)

    var polygonPath = UIBezierPath()
    polygonPath.moveToPoint(CGPointMake(0, -145.95))
    polygonPath.addLineToPoint(CGPointMake(126.4, -72.98))
    polygonPath.addLineToPoint(CGPointMake(126.4, 72.97))
    polygonPath.addLineToPoint(CGPointMake(0, 145.95))
    polygonPath.addLineToPoint(CGPointMake(-126.4, 72.98))
    polygonPath.addLineToPoint(CGPointMake(-126.4, -72.97))
    polygonPath.closePath()
    CGContextSaveGState(context)
    polygonPath.addClip()
    CGContextDrawLinearGradient(context, sVGID_1_2,
        CGPointMake(-126.4, -72.97),
        CGPointMake(126.41, 72.99),
        UInt32(kCGGradientDrawsBeforeStartLocation) | UInt32(kCGGradientDrawsAfterEndLocation))
    CGContextRestoreGState(context)

to produce this:

在此输入图像描述

If this doesn't work, then tell me, I will try to fix it up to make it work.

Also, here's just a simple rectangle:

let context = UIGraphicsGetCurrentContext()

let gradientColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)
let gradientColor2 = UIColor(red: 0.988, green: 0.933, blue: 0.129, alpha: 1.000)
let gradientColor3 = UIColor(red: 1.000, green: 0.000, blue: 1.000, alpha: 1.000)

let sVGID_1_3 = CGGradientCreateWithColors(CGColorSpaceCreateDeviceRGB(), [gradientColor.CGColor, gradientColor2.CGColor, gradientColor3.CGColor], [0, 0.43, 1])

let rectanglePath = UIBezierPath(rect: CGRectMake(68, 28, 78.4, 78.4))
CGContextSaveGState(context)
rectanglePath.addClip()
CGContextDrawLinearGradient(context, sVGID_1_3,
    CGPointMake(68, 67.19),
    CGPointMake(146.38, 67.19),
    UInt32(kCGGradientDrawsBeforeStartLocation) | UInt32(kCGGradientDrawsAfterEndLocation))
CGContextRestoreGState(context)

在此输入图像描述

here's a function to try out, you'll have to mess around with the colors, but you can input a frame:

class func drawStuff(#frame: CGRect) {
        let context = UIGraphicsGetCurrentContext()

        let gradientColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)
        let gradientColor2 = UIColor(red: 0.988, green: 0.933, blue: 0.129, alpha: 1.000)
        let gradientColor3 = UIColor(red: 1.000, green: 0.000, blue: 1.000, alpha: 1.000)

        let sVGID_1_4 = CGGradientCreateWithColors(CGColorSpaceCreateDeviceRGB(), [gradientColor.CGColor, gradientColor2.CGColor, gradientColor3.CGColor], [0, 0.43, 1])

        let rectangleRect = CGRectMake(frame.origin.x, frame.origin.y, frame.size.width, frame.size.height)
        let rectanglePath = UIBezierPath(rect: rectangleRect)
        CGContextSaveGState(context)
        rectanglePath.addClip()
        CGContextDrawLinearGradient(context, sVGID_1_4, CGPointMake(rectangleRect.minX, rectangleRect.midY),CGPointMake(rectangleRect.maxX, rectangleRect.midY), 0)
        CGContextRestoreGState(context)
    }

或者您可以使用我自己写的:) https://github.com/BilalReffas/SwiftyGradient

Swift 4 version of the first shape from @Loxx answer above which produces this output:

@Loxx形状#1 .

private func drawShape() {
    guard let context = UIGraphicsGetCurrentContext() else {
        print("No context, handle me")
        return
    }

    let gradientColor3 = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)
    let gradientColor4 = UIColor(red: 0.000, green: 1.000, blue: 0.000, alpha: 1.000)

    guard let sVGID_1_2 = CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(), colors: [gradientColor3.cgColor, gradientColor4.cgColor] as CFArray, locations: [0, 1]) else {
        print("No gradient, handle me")
        context.restoreGState()
        return
    }

    context.saveGState()
    context.translateBy(x: 198.95, y: 199.4)
    context.rotate(by: -30 * CGFloat(Double.pi) / 180)

    let polygonPath = UIBezierPath()
    polygonPath.move(to: CGPoint(x: 0, y: -145.95))
    polygonPath.addLine(to: CGPoint(x: 126.4, y: -72.98))
    polygonPath.addLine(to: CGPoint(x: 126.4, y: 72.97))
    polygonPath.addLine(to: CGPoint(x: 0, y: 145.95))
    polygonPath.addLine(to: CGPoint(x: -126.4, y: 72.98))
    polygonPath.addLine(to: CGPoint(x: -126.4, y: -72.97))
    polygonPath.close()
    context.saveGState()
    polygonPath.addClip()
    context.drawLinearGradient(sVGID_1_2,
                               start: CGPoint(x: -126.4, y: -72.97),
                               end: CGPoint(x: 126.41, y: 72.99),
                               options: [.drawsBeforeStartLocation, .drawsAfterEndLocation])
    context.restoreGState()
}

I'm posting it because basically every line has changed in conversion to Swift 4 and it took a while to convert it just to check if it works.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM