使用 Beizepath 在 UIView 上绘制形状?

Draw shape on UIView using Beizepath?

如何绘制如下图的背景路径?

我正在尝试我自己但安全不正常

在我的代码下方:

public class ShapeView : UIView{
    public override func draw(_ rect: CGRect) {
        let context = UIGraphicsGetCurrentContext()!
        
        let corneRadius : CGFloat = 40.0

        //// Color Declarations
        let fillColor = UIColor(red: 0.125, green: 0.259, blue: 0.443, alpha: 1.000)
        let fillColor2 = UIColor(red: 0.153, green: 0.314, blue: 0.525, alpha: 1.000)

        //// g10
        //// g12
        //// path14 Drawing
        context.saveGState()
        context.translateBy(x: rect.origin.x, y: rect.origin.y)
//        context.rotate(by: 0.8 * CGFloat.pi/180)

        let path14Path = UIBezierPath(roundedRect: CGRect(x: rect.origin.x, y: rect.origin.y, width: rect.size.width, height: rect.size.height), cornerRadius: CGFloat(corneRadius))
        fillColor.setFill()
        path14Path.fill()

        context.restoreGState()


        //// g16
        //// g18
        //// g24
        //// path30 Drawing


        //// path32 Drawing
//        let path32Path = UIBezierPath()
//        path32Path.move(to: CGPoint(x: 0, y:0))
////        path32Path.addLine(to: CGPoint(x: 5.15, y: corneRadius))
////        path32Path.addCurve(to: CGPoint(x: corneRadius + 5 , y: 0), controlPoint1: CGPoint(x: corneRadius + 5 , y: corneRadius ), controlPoint2: CGPoint.zero)
////        path32Path.addCurve(to: CGPoint(x: 50.59, y: 5.01), controlPoint1: CGPoint(x: 50.53, y: 5.01), controlPoint2: CGPoint(x: 50.56, y: 5.01))
//        path32Path.addLine(to: CGPoint(x: rect.width * 0.3, y: 0))  // top left x and y position of first shape
//        path32Path.addLine(to: CGPoint(x: 0, y: rect.height * 0.5))
//        path32Path.close()
//        fillColor2.setFill()
//        path32Path.fill()
        
        
//        let path14Path = UIBezierPath(roundedRect: CGRect(x: -321.25, y: -190.1, width: 642.5, height: 380.2), cornerRadius: 44.6)

        
//        let path30Path = UIBezierPath()
//        path30Path.move(to: CGPoint(x: 182.31, y: 385.45))
//        path30Path.addLine(to: CGPoint(x: 45.15, y: 385.16))
//        path30Path.addCurve(to: CGPoint(x: 17.03, y: 374.93), controlPoint1: CGPoint(x: 34.43, y: 385.13), controlPoint2: CGPoint(x: 24.65, y: 381.31))
//        path30Path.addLine(to: CGPoint(x: 329.68, y: 5.61))
//        path30Path.addLine(to: CGPoint(x: 503.55, y: 5.99))
//        path30Path.addLine(to: CGPoint(x: 182.31, y: 385.45))
//        path30Path.close()
//        fillColor2.setFill()
//        path30Path.fill()
        
        
        
        
        let path30Path = UIBezierPath()
        path30Path.move(to: CGPoint(x: rect.width * 0.5, y: rect.height - 10))
        path30Path.addLine(to: CGPoint(x: 10, y: rect.height  -  10 ))
//        path30Path.addCurve(to: CGPoint(x: corneRadius , y: 374.93), controlPoint1: CGPoint(x: rect.width * 0.5 * 2, y: 385.13), controlPoint2: CGPoint(x: rect.width * 0.5, y: 381.31))
        
        path30Path.addLine(to: CGPoint(x: rect.width * 0.8, y: 0))
        path30Path.addLine(to: CGPoint(x: rect.width * 0.35, y: rect.height))
        path30Path.addLine(to: CGPoint(x: rect.width * 0.5, y: rect.height ))

        path30Path.close()
        fillColor2.setFill()
        path30Path.fill()

        


        //// path34 Drawing
//        let path34Path = UIBezierPath()
//        path34Path.move(to: CGPoint(x: 283.03, y: 385.67))
//        path34Path.addLine(to: CGPoint(x: 208.2, y: 385.51))
//        path34Path.addLine(to: CGPoint(x: 529.44, y: 6.04))
//        path34Path.addLine(to: CGPoint(x: 603.49, y: 6.2))
//        path34Path.addCurve(to: CGPoint(x: 604.26, y: 6.21), controlPoint1: CGPoint(x: 603.75, y: 6.2), controlPoint2: CGPoint(x: 604.01, y: 6.21))
//        path34Path.addLine(to: CGPoint(x: 283.03, y: 385.67))
//        path34Path.close()
//        fillColor2.setFill()
//        path34Path.fill()
//
//
//        //// path36 Drawing
//        let path36Path = UIBezierPath()
//        path36Path.move(to: CGPoint(x: 522.68, y: 386.19))
//        path36Path.addLine(to: CGPoint(x: 447.85, y: 386.03))
//        path36Path.addLine(to: CGPoint(x: 646.21, y: 151.71))
//        path36Path.addLine(to: CGPoint(x: 644.92, y: 241.79))
//        path36Path.addLine(to: CGPoint(x: 522.68, y: 386.19))
//        path36Path.close()
//        fillColor2.setFill()
//        path36Path.fill()
    }
}

我想画那些背景光的颜色,形状。

输出:

对于你的情况,你的体形是有规律的。

要处理坐标数学,最好使用基于矩形高度的相关列表坐标。

public class ShapeView : UIView{
    public override func draw(_ rect: CGRect) {
        let list: [CGFloat] = [0, 0.5, 1, 1.6, 1.7, 2, 2.5, 2.7, 3]
        let coordinates = list.map { (i) -> CGFloat in
            rect.height * i
        }
        var i = 1
        let shallow = UIColor(red: 0.125, green: 0.259, blue: 0.443, alpha: 1.000)
        let deep = UIColor(red: 0.153, green: 0.314, blue: 0.525, alpha: 1.000)
        while i < list.count {
            let b = UIBezierPath()
            b.move(to: CGPoint(x: 0, y: coordinates[i - 1]))
            b.addLine(to: CGPoint(x: 0, y: coordinates[i]))
            b.addLine(to: CGPoint(x: coordinates[i], y: 0))
            b.addLine(to: CGPoint(x: coordinates[i - 1], y: 0))
            b.close()
            if i % 2 == 1{
                shallow.setFill()
            }
            else{
                deep.setFill()
            }
            b.fill()
            i += 1
        }
    }
}

要处理左角,您可以使用layer.cornerRadius

这样调用:

  let v = ShapeView()
  v.frame = CGRect(x: 50, y: 100, width: 300, height: 150)
  v.layer.cornerRadius = 30
  v.clipsToBounds = true
  view.addSubview(v)

改进@dengApro 的好答案。

可以使用其他方式处理角,

只画角,不用layer.cornerRadius

public class ShapeView : UIView{
    public override func draw(_ rect: CGRect) {

        let list: [CGFloat] = [0, 0.5, 1, 1.6, 1.7, 2, 2.5, 2.7, 3]
        let coordinates = list.map { (i) -> CGFloat in
            rect.height * i
        }
        var i = 1
        while i < list.count {
            let b = UIBezierPath()
            b.move(to: CGPoint(x: 0, y: coordinates[i - 1]))
            b.addLine(to: CGPoint(x: 0, y: coordinates[i]))
            b.addLine(to: CGPoint(x: coordinates[i], y: 0))
            b.addLine(to: CGPoint(x: coordinates[i - 1], y: 0))
            b.close()
            if i % 2 == 1{
                UIColor.blue.setFill()
            }
            else{
                UIColor.green.setFill()
            }
            b.fill()
            i += 1
        }
        
        
        // for corners
        let upLhs = CGPoint(x: 0, y: 0)
        let upRhs = CGPoint(x: rect.width, y: 0)
        let downRhs = CGPoint(x: rect.width, y: rect.height)
        let downLhs = CGPoint(x: 0, y: rect.height)
        let corner: CGFloat = 30
        let upLhsArc = UIBezierPath()
        upLhsArc.move(to: upLhs)
        upLhsArc.addLine(to: upLhs + .x(corner))
        upLhsArc.addArc(withCenter: upLhs ~> PointOffset(x: corner, y: corner), radius: corner, startAngle: .pi * 1.5, endAngle: .pi, clockwise: false)
        upLhsArc.close()
        
        let upRhsArc = UIBezierPath()
        upRhsArc.move(to: upRhs)
        upRhsArc.addLine(to: upRhs + .x(corner * -1))
        upRhsArc.addArc(withCenter: upRhs ~> PointOffset(x: corner * -1, y: corner), radius: corner, startAngle: .pi * -0.5, endAngle: 0, clockwise: true)
        upRhsArc.close()
        
        let downRhsArc = UIBezierPath()
        downRhsArc.move(to: downRhs)
        downRhsArc.addLine(to: downRhs + .x(corner * -1))
        downRhsArc.addArc(withCenter: downRhs ~> PointOffset(x: corner * -1, y: corner * -1), radius: corner, startAngle: .pi * 0.5, endAngle: 0, clockwise: false)
        downRhsArc.close()
        
        let downLhsArc = UIBezierPath()
        downLhsArc.move(to: downLhs)
        downLhsArc.addLine(to: downLhs + .x(corner))
        downLhsArc.addArc(withCenter: downLhs ~> PointOffset(x: corner , y: corner * -1), radius: corner, startAngle: .pi * 0.5, endAngle: .pi, clockwise: true)
        downLhsArc.close()
        // use your background color
        UIColor.white.setFill()
        upLhsArc.fill()
        upRhsArc.fill()
        downRhsArc.fill()
        downLhsArc.fill()
    }
}

这样调用:

override func viewDidLoad() {
     super.viewDidLoad()
     let v = ShapeView()
     v.frame = CGRect(x: 50, y: 100, width: 300, height: 150)
     view.addSubview(v)
 }

帮助功能:

struct PointOffset{
    let x: CGFloat
    let y: CGFloat
}


enum PointDimension{
    case x(CGFloat), y(CGFloat)
}


func +(left: CGPoint, right: PointDimension) -> CGPoint {
    switch right {
    case .x(let v):
        return CGPoint(x: left.x + v, y: left.y)
    case .y(let v):
        return CGPoint(x: left.x, y: left.y + v)
    }
}


infix operator ~>: AdditionPrecedence
func ~>(left: CGPoint, right: PointOffset) -> CGPoint{
    return CGPoint(x: left.x + right.x, y: left.y + right.y)
}

这是改进@dengApro 漂亮代码的另一种方法。

改进渲染性能。

@dengApro 的代码很好,在一些额外的地方填充了颜色。

然后做一些计算以避免额外的工作。

// coordinates's structure

// [[CGPoint]], 
// an array of an array , the inner array has two points
// the first point is at top, the second point is at bottom
public class ShapeView : UIView{
    public override func draw(_ rect: CGRect) {
        let list: [CGFloat] = [0, 0.5, 1, 1.6, 1.7, 2, 2.5, 2.7, 3]
        let h = rect.height
        let coordinates = list.reduce([[CGPoint]]()) { (result, val) -> [[CGPoint]] in
            if result.isEmpty{
                return [[CGPoint(x: val, y: val), CGPoint(x: val, y: val)]]
            }
            else{
                let x = h * val
                let first = CGPoint(x: x, y: 0)
                let y = h * min(1, val)
                var secondX: CGFloat = 0
                if val > 1{
                    secondX = x - h
                }
                return result + [[first, CGPoint(x: secondX, y: y)]]
            }
            
        }
        
        var i = 1
        let shallow = UIColor(red: 0.125, green: 0.259, blue: 0.443, alpha: 1.000)
        let deep = UIColor(red: 0.153, green: 0.314, blue: 0.525, alpha: 1.000)
        while i < list.count {
            let b = UIBezierPath()
            b.move(to: coordinates[i-1][0])
            b.addLine(to: coordinates[i][0])
            b.addLine(to: coordinates[i][1])
            b.addLine(to: coordinates[i-1][1])
           // print(coordinates[i-1][0],  coordinates[i][0],  coordinates[i][1], coordinates[i-1][1])
            b.close()
            if i % 2 == 1{
                shallow.setFill()
            }
            else{
                deep.setFill()
            }
            b.fill()
            i += 1
        }
    }
}

这样调用:

    let v = ShapeView()
    v.frame = CGRect(x: 50, y: 100, width: 300, height: 150)
    v.layer.cornerRadius = 30
    v.clipsToBounds = true
    view.addSubview(v)

改进@dengST30 的代码,

改进积分计算reduce方法:

public class ShapeView : UIView{
    public override func draw(_ rect: CGRect) {
        let list: [CGFloat] = [0.5, 1, 1.6, 1.7, 2, 2.5, 2.7, 3]
        let h = rect.height
        let start = [CGPoint](repeating: CGPoint.zero, count: 2)
        let points = list.reduce([start]) { (result, val) -> [[CGPoint]] in
            let x = h * val
            let y = h * min(1, val)
            var secondX: CGFloat = 0
            if val > 1{
                secondX = x - h
            }
            return result + [[CGPoint(x: x, y: 0), CGPoint(x: secondX, y: y)]]
        }
        var i = 1
        while i < points.count {
            let b = UIBezierPath()
            b.move(to: points[i-1][0])
            b.addLine(to: points[i][0])
            b.addLine(to: points[i][1])
            b.addLine(to: points[i-1][1])
            b.close()
            if i % 2 == 1{
                UIColor.black.setFill()
            }
            else{
                UIColor.lightText.setFill()
            }
            b.fill()
            i += 1
        }
    }
}