【问题标题】:gradient in for multipel UIBezierPath多个 UIBezierPath 的渐变
【发布时间】:2018-12-02 15:16:41
【问题描述】:

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

 @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

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

有人有什么建议吗?

【问题讨论】:

  • 你只需要一个视图和一个draw方法来渲染这四个渐变。

标签: ios swift core-graphics gradient uibezierpath


【解决方案1】:

这有点类似于 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

【讨论】:

  • 你能解释一下代码的逻辑吗?你的代码和Codo的代码有什么区别?
  • 这个想法是将图像分隔成 4 个不同的渐变线,这样您就可以用 4 个三角形绘制它。然后第一个剪辑区域将这 4 个三角形限制为屏幕上应填充渐变的部分。我的代码和 Codo 的主要区别在于绘图关键点的计算。无论视图有多大,我的代码都可以正常工作。
  • 感谢您的帮助,是否有任何我可以阅读更多有关此主题的文档?
  • 你指的是哪个主题?显然,所涉及的每个类/结构都有参考文档。
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

    这是一个可以直接在操场上运行的示例。

    由于您需要四个渐变,并且渐变是使用剪裁绘制的,因此图形上下文会被保存和恢复多次(以重置剪裁)。

    放坡起点和终点是剪裁角之一。那是不需要的。您可以(并且可能)应该使用单独的点。为了达到预期的效果,您有时需要在剪裁区域之外的地方使用起点或终点。

    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 决定只需要重绘视图的一部分,那么您的代码将绘制错误的形状。

    【讨论】:

    • 请注意,draw 中的代码取决于正在创建的具有特定大小的视图。它不会随着视图大小的变化而缩放,至少在垂直方向上是这样。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-27
    • 1970-01-01
    相关资源
    最近更新 更多