Search code examples
iosswiftcore-graphicsgradientuibezierpath

gradient in for multipel UIBezierPath


I'm supposed to create this.

this

I did search the google, youtube, and StackOverflow, and the code below is the result of my research.

     @IBDesignable class TriangleView2: UIView {
    
    override init(frame: CGRect) {
        super.init(frame: frame)
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    let gradient = CAGradientLayer()
    override func draw(_ rect: CGRect) {
 
        //draw the line of UIBezierPath
        
        let path1 = UIBezierPath()
        path1.move(to: CGPoint(x: rect.minX - 100, y: rect.maxY - 80))
        path1.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
        path1.addLine(to: CGPoint(x: (rect.maxX + 90  ), y: rect.minY/2 ))
        path1.close()

        // add clipping path. this draws an imaginary line (to create bounds) from the
        //ends of the UIBezierPath line down to the bottom of the screen
        let clippingPath = path1.copy() as! UIBezierPath
        clippingPath.move(to: CGPoint(x: rect.minX - 100, y: rect.maxY - 80))
        clippingPath.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
        clippingPath.addLine(to: CGPoint(x: (rect.maxX + 90  ), y: rect.minY/2 ))
        clippingPath.close()
        
        clippingPath.addClip()
        
        // create and add the gradient
        let colors = [theme.current.profile_start_view1.cgColor, theme.current.profile_end_view1.cgColor]
        
        let colorSpace = CGColorSpaceCreateDeviceRGB()        
        let colorLocations:[CGFloat] = [0.0, 1.0]        
        let gradient = CGGradient(colorsSpace: colorSpace,
                                  colors: colors as CFArray,
                                  locations: colorLocations)
        
        let context = UIGraphicsGetCurrentContext()
        let startPoint = CGPoint(x: 1, y: 1)
        let endPoint = CGPoint(x: 1, y: bounds.maxY)
        // and lastly, draw the gradient.
        context!.drawLinearGradient(gradient!, start: startPoint, end: 
      endPoint, options: CGGradientDrawingOptions.drawsAfterEndLocation)
        }
    }

Right not I have 2 views ( will be 3 if I could complete it) with some differences.

The result is this.

this

These 2 views do not have the same colour, but as you can see both views have the same gradient with the same direction.

Does anyone have any suggestion?


Solution

  • This is somewhat similar to Codo's answer but you only need 4 points.

    class FourGradientsView: UIView {
        override func draw(_ rect: CGRect) {
            let ctx = UIGraphicsGetCurrentContext()!
    
            // Points of area to draw - adjust these 4 variables as needed
            let tl = CGPoint(x: 0, y: 0)
            let tr = CGPoint(x: bounds.width * 1.3, y: 0)
            let bl = CGPoint(x: -bounds.width * 1.8, y: bounds.height * 1.4)
            let br = CGPoint(x: bounds.width * 1.3, y: bounds.height * 2)
    
            // Find the intersection of the two crossing diagonals
            let s1x = br.x - tl.x
            let s1y = br.y - tl.y
            let s2x = tr.x - bl.x
            let s2y = tr.y - bl.y
            //let s = (-s1y * (tl.x - bl.x) + s1x * (tl.y - bl.y)) / (-s2x * s1y + s1x * s2y)
            let t = ( s2x * (tl.y - bl.y) - s2y * (tl.x - bl.x)) / (-s2x * s1y + s1x * s2y)
            let center = CGPoint(x: tl.x + (t * s1x), y: tl.y + (t * s1y))
    
            // Create clipping region to avoid drawing where we don't want any gradients
            ctx.saveGState()
            let clip = CGPoint(x: 0, y: bounds.height * 0.7)
            let clipPath = UIBezierPath()
            clipPath.move(to: CGPoint(x: 0, y: 0))
            clipPath.addLine(to: clip)
            clipPath.addLine(to: CGPoint(x: bounds.width, y: bounds.height))
            clipPath.addLine(to: CGPoint(x: bounds.width, y: 0))
            clipPath.close()
            clipPath.addClip()
    
            // Use these two colors for all 4 gradients (adjust as needed)
            let colors = [
                UIColor(hue: 120/360, saturation: 1, brightness: 0.85, alpha: 1).cgColor,
                UIColor(hue: 120/360, saturation: 1, brightness: 0.3, alpha: 1).cgColor
            ] as CFArray
    
            // The common gradient
            let gradient = CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(), colors: colors, locations: nil)!
    
            // Top gradient
            ctx.saveGState()
            let pathTop = UIBezierPath()
            pathTop.move(to: tl)
            pathTop.addLine(to: tr)
            pathTop.addLine(to: center)
            pathTop.close()
            pathTop.addClip()
    
            ctx.drawLinearGradient(gradient, start: CGPoint(x: bounds.width, y: 0), end: CGPoint(x: 0, y: 0), options: [])
            ctx.restoreGState()
    
            // Right gradient
            ctx.saveGState()
            let pathRight = UIBezierPath()
            pathRight.move(to: tr)
            pathRight.addLine(to: br)
            pathRight.addLine(to: center)
            pathRight.close()
            pathRight.addClip()
    
            ctx.drawLinearGradient(gradient, start: CGPoint(x: bounds.width, y: bounds.height), end: CGPoint(x: bounds.width, y: 0), options: [])
            ctx.restoreGState()
    
            // Bottom gradient
            ctx.saveGState()
            let pathBottom = UIBezierPath()
            pathBottom.move(to: br)
            pathBottom.addLine(to: bl)
            pathBottom.addLine(to: center)
            pathBottom.close()
            pathBottom.addClip()
    
            ctx.drawLinearGradient(gradient, start: CGPoint(x: 0, y: bounds.height), end: CGPoint(x: bounds.width, y: bounds.height), options: [])
            ctx.restoreGState()
    
            // Left gradient
            ctx.saveGState()
            let pathLeft = UIBezierPath()
            pathLeft.move(to: tl)
            pathLeft.addLine(to: bl)
            pathLeft.addLine(to: center)
            pathLeft.close()
            pathLeft.addClip()
    
            ctx.drawLinearGradient(gradient, start: CGPoint(x: 0, y: 0), end: CGPoint(x: 0, y: bounds.height), options: [])
            ctx.restoreGState()
    
            ctx.restoreGState()
        }
    }
    
    let grView = FourGradientsView(frame: CGRect(x: 0, y: 0, width: 320, height: 320))
    grView.backgroundColor = .black