多个 UIBezierPath 的渐变

gradient in for multipel UIBezierPath

我应该创建 this。 我确实搜索了 google、youtube 和 Whosebug,下面的代码是我研究的结果。

 @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)
    }
}

对不对,我有 2 个视图(如果我能完成的话,将是 3 个),但有一些不同。结果是 this

这 2 个视图的颜色不同,但正如您所见,两个视图的渐变方向相同

有人有什么建议吗?

您编写的代码始终使用相同的起点和终点颜色,始终使用相同的颜色位置,并且始终使用相同的起点和终点。当然,梯度具有相同方向的相同梯度。

为您的视图提供渐变起点和终点属性以及起点和终点颜色。根据视图的边界,在视图控制器的 layoutDidChange() 方法中设置渐变视图的渐变起点。 (这样你就可以正确处理设备旋转和不同尺寸的设备。

这是一个示例,您可以直接在 playground 中运行。

因为你想要四个渐变并且渐变是使用剪裁绘制的,所以图形上下文被保存并恢复了几次(以重置剪裁)。

放码起止点为剪角之一。那是不需要的。您可以(并且可能)应该使用单独的点。为获得所需的结果,有时您希望在裁剪区域之外使用起点或终点。

import UIKit
import PlaygroundSupport


class TriangleView2: UIView {

    override init(frame: CGRect) {
        super.init(frame: frame)
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    override func draw(_ rect: CGRect) {

        let colors = [UIColor(red: 50/255.0, green: 242/255.0, blue: 111/255.0, alpha: 1).cgColor,
                      UIColor(red: 29/255.0, green: 127/255.0, blue: 60/255.0, alpha: 1).cgColor]
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let colorLocations:[CGFloat] = [0.0, 1.0]
        let gradient = CGGradient(colorsSpace: colorSpace,
                                  colors: colors as CFArray,
                                  locations: colorLocations)!
        let options: CGGradientDrawingOptions = [CGGradientDrawingOptions.drawsBeforeStartLocation, CGGradientDrawingOptions.drawsAfterEndLocation]

        let p1 = CGPoint(x: 0, y: 0)
        let p2 = CGPoint(x: bounds.width, y: 0)
        let p3 = CGPoint(x: bounds.width, y: 20)
        let p4 = CGPoint(x: bounds.width / 3, y: 140)
        let p5 = CGPoint(x: 0, y: 200)
        let p6 = CGPoint(x: bounds.width * 5 / 8, y: 260)
        let p7 = CGPoint(x: 0, y: 230)
        let p8 = CGPoint(x: bounds.width, y: 280)

        let context = UIGraphicsGetCurrentContext()!

        context.saveGState()
        let path1 = UIBezierPath()
        path1.move(to: p1)
        path1.addLine(to: p2)
        path1.addLine(to: p3)
        path1.addLine(to: p4)
        path1.close()
        path1.addClip()
        context.drawLinearGradient(gradient, start: p3, end: p1, options: options)
        context.restoreGState()

        context.saveGState()
        let path2 = UIBezierPath()
        path2.move(to: p1)
        path2.addLine(to: p4)
        path2.addLine(to: p5)
        path2.close()
        path2.addClip()
        context.drawLinearGradient(gradient, start: p1, end: p5, options: options)
        context.restoreGState()

        context.saveGState()
        let path3 = UIBezierPath()
        path3.move(to: p3)
        path3.addLine(to: p8)
        path3.addLine(to: p6)
        path3.addLine(to: p4)
        path3.close()
        path3.addClip()
        context.drawLinearGradient(gradient, start: p8, end: p3, options: options)
        context.restoreGState()

        context.saveGState()
        let path4 = UIBezierPath()
        path4.move(to: p5)
        path4.addLine(to: p4)
        path4.addLine(to: p6)
        path4.addLine(to: p7)
        path4.close()
        path4.addClip()
        context.drawLinearGradient(gradient, start: p7, end: p6, options: options)
        context.restoreGState()
    }
}

let main = TriangleView2(frame: CGRect(x: 0, y: 0, width: 320, height: 500))
PlaygroundPage.current.liveView = main

更新

还有一件事:不要使用 rect 参数来导出形状的几何形状。 rect 不指视图大小或位置。相反,它是需要重新绘制的区域。如果 iOS 决定只有部分视图需要重绘,您的代码将绘制错误的形状。

这与Codo的回答有点相似,但你只需要4分。

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