Search code examples
iosswiftcore-graphics

How to create a gradiated triangle image


Here's the code I have to create a gradiated triangle (a triangle that, instead of being a flat color, has a color gradient in it):

extension UIImage {

    struct GradientPoint {
        var location: CGFloat
        var color: UIColor
    }

    static func gradiatedTriangle(side: CGFloat)->UIImage {

        UIGraphicsBeginImageContextWithOptions(CGSize(width: side, height: side), false, 0)
        let ctx = UIGraphicsGetCurrentContext()!
        ctx.saveGState()

        //create gradient and draw it
        let gradientPoints = [UIImage.GradientPoint(location: 0, color: UIColor.from(rgb: 0xff0000)), UIImage.GradientPoint(location: 1, color: UIColor.from(rgb: 0xd0d0d0))]
        let gradient = CGGradient(colorSpace: CGColorSpaceCreateDeviceRGB(), colorComponents: gradientPoints.flatMap{$0.color.cgColor.components}.flatMap{$0}, locations: gradientPoints.map{$0.location}, count: gradientPoints.count)!
        ctx.drawLinearGradient(gradient, start: CGPoint.zero, end: CGPoint(x: 0, y: side), options: CGGradientDrawingOptions())

        //draw triangle
        ctx.beginPath()
        ctx.move(to: CGPoint(x: side / 2, y: side))
        ctx.addLine(to: CGPoint(x: 0, y: 0))
        ctx.addLine(to: CGPoint(x: side, y: 0))
        ctx.closePath()
        ctx.drawPath(using: .fill)

        ctx.restoreGState()
        let img = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()

        return img
    }
}

However, the image that's returned has a square gradient in the background and a black triangle on top of it. I can make the fill clear, but I don't know how to trim the gradient layer around the path so that only a triangle remains. How can I trim away the gradient layer that's outside the path I drew?


Solution

  • Replace your code with this one, first you need to add the path, then ctx.clip() to clip the context and then draw your gradient

    import UIKit
    
    extension UIImage {
    
        struct GradientPoint {
            var location: CGFloat
            var color: UIColor
        }
    
        static func gradiatedTriangle(side: CGFloat)->UIImage {
    
            UIGraphicsBeginImageContextWithOptions(CGSize(width: side, height: side), false, 0)
            let ctx = UIGraphicsGetCurrentContext()!
    
            //draw triangle
            ctx.beginPath()
            ctx.move(to: CGPoint(x: side / 2, y: side))
            ctx.addLine(to: CGPoint(x: 0, y: 0))
            ctx.addLine(to: CGPoint(x: side, y: 0))
            ctx.closePath()
            ctx.clip()
    
            //create gradient and draw it
            let gradientPoints = [UIImage.GradientPoint(location: 0, color: UIColor.red), UIImage.GradientPoint(location: 1, color: UIColor.blue)]
            let gradient = CGGradient(colorSpace: CGColorSpaceCreateDeviceRGB(), colorComponents: gradientPoints.flatMap{$0.color.cgColor.components}.flatMap{$0}, locations: gradientPoints.map{$0.location}, count: gradientPoints.count)!
            ctx.drawLinearGradient(gradient, start: CGPoint.zero, end: CGPoint(x: 0, y: side), options: CGGradientDrawingOptions())
    
    
            let img = UIGraphicsGetImageFromCurrentImageContext()!
            UIGraphicsEndImageContext()
    
            return img
        }
    }
    

    Result

    enter image description here

    Hope this helps you, Best regards