如何在 SwiftUI 中绘制角度和弧线

akd*_*uza 1 ios swift swiftui

我正在尝试画一个相当基本的鸟形状。我使用圆圈作为身体,并尝试绘制上喙(以红色突出显示),如下面的参考图所示。

图片参考:

在此输入图像描述

struct Beak: Shape {
    func path(in rect: CGRect) -> Path {
        var path = Path()
        path.move(to: CGPoint(x: rect.width/4, y: rect.height/2))
        path.addLine(to: CGPoint(x: 0, y: rect.height/2))
        // draw a line back to the circumference at a given angle
        // & close it with an arc along the circumference of the Body circle
        path.addLine(to: ??) 
        return path
    }
}

struct BirdView: View {
    var body: some View {
        ZStack {
            // MARK: Body
            Circle()
                .stroke(lineWidth: 10)
                .frame(width: 150)
                .overlay(alignment: .center) {
                    Circle()
                        .fill(.yellow)
                }
            
            // MARK: Beak
            Beak()
                .stroke(lineWidth: 5)
            
        }
        .frame(width: 300, height: 300)
        .background(.orange)
    }
}
Run Code Online (Sandbox Code Playgroud)

我想要实现的目标是让喙也沿着身体圆的圆周以弧线关闭,以便我可以稍后通过旋转对其进行动画处理以模拟喙打开/关闭。任何帮助表示赞赏。

Ben*_*eez 5

如果您用来.addArc绘制弯曲的位,那么就没什么了不起的。如果您使用中点作为圆弧的起点,然后使用 将最终形状移动到位,这可能会更容易.offset。这也为后面添加旋转效果做好了准备。

为了将鸟头的特征保持在一起,您可以将它们全部作为叠加层应用到基圆上,也可以将它们构建在单独的ZStack. 单独进行ZStack可能更简单,框架尺寸ZStack可用于确定圆圈以及眼睛和喙的尺寸。

这是改编后的Shape

struct Beak: Shape {
    let angleDegrees: CGFloat
    func path(in rect: CGRect) -> Path {
        var path = Path()
        path.move(to: CGPoint(x: rect.midX, y: rect.midY))
        path.addArc(
            center: CGPoint(x: rect.maxX, y: rect.midY),
            radius: rect.midX,
            startAngle: .degrees(180),
            endAngle: .degrees(180 + angleDegrees),
            clockwise: angleDegrees < 0
        )
        path.addLine(to: CGPoint(x: 0, y: rect.midY))
        path.closeSubpath()
        return path
    }
}
Run Code Online (Sandbox Code Playgroud)

以下是如何使用它。我无法抗拒将其动画化

struct BirdView: View {
    @State private var gapeAngle = Angle.zero

    private func birdHead(diameter: CGFloat) -> some View {
        ZStack {
            Circle()
                .fill(.yellow)

            Circle()
                .stroke(lineWidth: 5)

            // Eye
            Ellipse()
                .fill(.black)
                .frame(width: diameter / 5, height: diameter / 5.55)
                .offset(x: -diameter / 4.7, y: -diameter / 5.35)

            // Upper beak
            Beak(angleDegrees: 20)
                .stroke(style: .init(lineWidth: 5, lineJoin: .round))
                .foregroundStyle(.red)
                .offset(x: -diameter / 2)
                .rotationEffect(gapeAngle)

            // Lower beak
            Beak(angleDegrees: -20)
                .stroke(style: .init(lineWidth: 5, lineJoin: .round))
                .foregroundStyle(.red)
                .offset(x: -diameter / 2)
                .rotationEffect(-gapeAngle)
        }
        .frame(width: diameter, height: diameter)
    }

    var body: some View {
        birdHead(diameter: 150)
            .padding(.leading, 50)
            .frame(width: 300, height: 300)
            .background(.orange)
            .onAppear {
                withAnimation(.easeInOut.repeatForever(autoreverses: true)) {
                    gapeAngle = .degrees(5)
                }
            }
    }
}
Run Code Online (Sandbox Code Playgroud)

动画片