SwiftUI绘制自定义形状的Path
SwiftUI绘制自定义形状的Path

SwiftUI绘制自定义形状的Path

在 SwiftUI 中,Path 是用于绘制自定义形状的基础组件。通过定义点和线的集合,可以创建复杂的矢量图形,如直线、曲线、矩形、圆形等。Path 是绘制自定义形状的核心工具,通常与 Shape 协同使用。

Path 的基本结构

Path 是一个结构体,可以通过以下几种方式构建路径:

1、手动构建路径

使用 Path 的构造函数添加点和线段,构建路径。

struct ContentView: View {
    var body: some View {
        Path { path in
            path.move(to: CGPoint(x: 50, y: 50))    // 移动到起点
            path.addLine(to: CGPoint(x: 200, y: 50)) // 添加一条线
            path.addLine(to: CGPoint(x: 200, y: 200)) // 添加另一条线
            path.closeSubpath()                    // 闭合路径
        }
        .stroke(Color.blue, lineWidth: 3) // 描边路径
    }
}

效果

一个蓝色的三角形路径。

2、内置形状辅助方法

Path 提供了许多简化绘制常见形状的方法,例如矩形、圆弧等。

struct ContentView: View {
    var body: some View {
        Path { path in
            path.addRect(CGRect(x: 50, y: 50, width: 100, height: 100)) // 添加矩形
            path.addEllipse(in: CGRect(x: 200, y: 50, width: 100, height: 100)) // 添加椭圆
        }
        .stroke(Color.green, lineWidth: 3)
    }
}

3、曲线绘制

Path 支持绘制曲线,例如二次贝塞尔曲线和三次贝塞尔曲线:

struct ContentView: View {
    var body: some View {
        Path { path in
            path.move(to: CGPoint(x: 50, y: 200))
            path.addQuadCurve(to: CGPoint(x: 250, y: 200), control: CGPoint(x: 150, y: 50)) // 二次贝塞尔曲线
            path.move(to: CGPoint(x: 50, y: 300))
            path.addCurve(to: CGPoint(x: 250, y: 300),
                          control1: CGPoint(x: 100, y: 100),
                          control2: CGPoint(x: 200, y: 500)) // 三次贝塞尔曲线
        }
        .stroke(Color.purple, lineWidth: 2)
    }
}

4、闭合路径

使用 .closeSubpath() 方法自动连接路径的起点和终点,形成一个闭合图形:

struct ContentView: View {
    var body: some View {
        Path { path in
            path.move(to: CGPoint(x: 100, y: 100))
            path.addLine(to: CGPoint(x: 200, y: 100))
            path.addLine(to: CGPoint(x: 150, y: 200))
            path.closeSubpath() // 自动闭合
        }
        .fill(Color.orange) // 填充路径
    }
}

结合 Shape 使用

通过实现 Shape 协议,可以将自定义路径抽象为可复用的形状组件。

struct Triangle: Shape {
    func path(in rect: CGRect) -> Path {
        var path = Path()
        path.move(to: CGPoint(x: rect.midX, y: rect.minY))
        path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
        path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
        path.closeSubpath()
        return path
    }
}

struct ContentView: View {
    var body: some View {
        Triangle()
            .stroke(Color.red, lineWidth: 4)
            .frame(width: 200, height: 200)
    }
}

Path 的核心方法

1、move(to:):将当前点移动到指定位置,不绘制线条。

通常用于初始化绘制的起点。

path.move(to: CGPoint(x: 50, y: 50)) // 移动到 (50, 50)

2、addLine(to:):从当前点到指定点绘制一条直线。

path.addLine(to: CGPoint(x: 150, y: 50)) // 绘制直线

3、addQuadCurve(to:control:):绘制二次贝塞尔曲线,指定终点和控制点。

path.addQuadCurve(to: CGPoint(x: 150, y: 100), control: CGPoint(x: 100, y: 50))

4、addCurve(to:control1:control2:):绘制三次贝塞尔曲线,指定终点和两个控制点。

path.addCurve(to: CGPoint(x: 150, y: 100),
    control1: CGPoint(x: 75, y: 50),
    control2: CGPoint(x: 125, y: 150))

5、addRect(_:):添加一个矩形到路径。

path.addRect(CGRect(x: 50, y: 50, width: 100, height: 100))

6、addEllipse(in:):添加一个椭圆或圆形到路径。

path.addEllipse(in: CGRect(x: 50, y: 50, width: 100, height: 50))

7、addArc(center:radius:startAngle:endAngle:clockwise:):绘制弧形或完整的圆。

path.addArc(center: CGPoint(x: 100, y: 100),
    radius: 50,
    startAngle: .degrees(0),
    endAngle: .degrees(270),
    clockwise: false)

8、closeSubpath():将当前路径闭合(起点和终点连接)。

path.closeSubpath() // 闭合路径

高级示例:组合路径

使用 Path 组合多个子路径:

struct ContentView: View {
    var body: some View {
        Path { path in
            path.addRect(CGRect(x: 50, y: 50, width: 100, height: 100))
            path.addEllipse(in: CGRect(x: 75, y: 200, width: 50, height: 50))
        }
        .stroke(Color.blue, lineWidth: 2)
    }
}

动态动画

与 AnimatableData 结合,创建动态路径:

struct AnimatableArc: Shape {
    var startAngle: Angle
    var endAngle: Angle
    var animatableData: AnimatablePair<Double, Double> {
        get { AnimatablePair(startAngle.radians, endAngle.radians) }
        set {
            startAngle = .radians(newValue.first)
            endAngle = .radians(newValue.second)
        }
    }
    
    func path(in rect: CGRect) -> Path {
        var path = Path()
        path.addArc(center: CGPoint(x: rect.midX, y: rect.midY),
                    radius: rect.width / 2,
                    startAngle: startAngle,
                    endAngle: endAngle,
                    clockwise: false)
        return path
    }
}

struct ContentView: View {
    @State private var progress: Double = 0.01
    
    var body: some View {
        AnimatableArc(startAngle: .degrees(0), endAngle: .degrees(360 * progress))
            .stroke(Color.green, lineWidth: 5)
            .frame(width: 200, height: 200)
            .onTapGesture {
                withAnimation(.linear(duration: 1)) {
                    progress = progress == 1 ? 0.01 : 1
                }
            }
    }
}

相关文章

1、SwiftUI内置形状:https://fangjunyu.com/2024/12/14/swiftui%e5%86%85%e7%bd%ae%e5%bd%a2%e7%8a%b6/

2、SwiftUI绘制边框stroke和strokeBorder:https://fangjunyu.com/2024/12/15/swiftui%e7%bb%98%e5%88%b6%e8%be%b9%e6%a1%86stroke%e5%92%8cstrokeborder/

3、SwiftUi定义尺寸和位置的CGRect结构: https://fangjunyu.com/2024/12/15/swiftui%e5%ae%9a%e4%b9%89%e5%b0%ba%e5%af%b8%e5%92%8c%e4%bd%8d%e7%bd%ae%e7%9a%84cgrect%e7%bb%93%e6%9e%84/

4、SwiftUi绘制边框stroke和strokeBorder: https://fangjunyu.com/2024/12/15/swiftui%e7%bb%98%e5%88%b6%e8%be%b9%e6%a1%86stroke%e5%92%8cstrokeborder/

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开放者。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注