Wie erstelle ich eine SwiftUI RoundedStar-Form?

Aug 30 2020

Dies ist eine selbst beantwortete Frage, die bei Stack Overflow durchaus akzeptabel (und sogar empfohlen) ist. Es geht darum, etwas Nützliches für andere zu teilen.

SwiftUI hat eine RoundedRectangle Shape. Es wäre schön, einen fünfzackigen Stern mit abgerundeten Spitzen zu haben, der zum Füllen, Ausschneiden und Animieren verwendet werden könnte.

Diese Antwort zum Stapelüberlauf zeigt, wie Sie RoundedStareine benutzerdefinierte UIViewVerwendung erstellen UIBezierPath.

Wie kann dieser Code angepasst werden SwiftUIals Shapedie animiert werden können?

Antworten

3 vacawama Aug 30 2020 at 01:04

Hier ist der RoundedStarCode, der als animierbare SwiftUI angepasst wurde Shape:

// Five-point star with rounded tips
struct RoundedStar: Shape {
    var cornerRadius: CGFloat
    
    var animatableData: CGFloat {
        get { return cornerRadius }
        set { cornerRadius = newValue }
    }
    
    func path(in rect: CGRect) -> Path {
        var path = Path()
        let center = CGPoint(x: rect.width / 2, y: rect.height / 2)
        let r = rect.width / 2
        let rc = cornerRadius
        let rn = r * 0.95 - rc
        
        // start angle at -18 degrees so that it points up
        var cangle = -18.0
        
        for i in 1 ... 5 {
            // compute center point of tip arc
            let cc = CGPoint(x: center.x + rn * CGFloat(cos(Angle(degrees: cangle).radians)), y: center.y + rn * CGFloat(sin(Angle(degrees: cangle).radians)))

            // compute tangent point along tip arc
            let p = CGPoint(x: cc.x + rc * CGFloat(cos(Angle(degrees: cangle - 72).radians)), y: cc.y + rc * CGFloat(sin(Angle(degrees: (cangle - 72)).radians)))

            if i == 1 {
                path.move(to: p)
            } else {
                path.addLine(to: p)
            }

            // add 144 degree arc to draw the corner
            path.addArc(center: cc, radius: rc, startAngle: Angle(degrees: cangle - 72), endAngle: Angle(degrees: cangle + 72), clockwise: false)

            // Move 144 degrees to the next point in the star
            cangle += 144
        }

        return path
    }
}

Der Code ist der UIBezierPathVersion sehr ähnlich, außer dass er den neuen AngleTyp verwendet, der einen einfachen Zugriff auf degreesund bietet radians. Der Code zum Zeichnen des gedrehten Sterns wurde entfernt, da es einfach ist, einer SwiftUI-Form mit dem .rotationEffect(angle:)Ansichtsmodifikator eine Drehung hinzuzufügen .


Demonstration:

Hier ist eine Demonstration, die die animierbaren Eigenschaften der cornerRadiusEinstellung zeigt und zeigt, wie die verschiedenen cornerRadiusEinstellungen auf einem Vollbildstern aussehen.

struct ContentView: View {
    @State private var radius: CGFloat = 0.0
    
    var body: some View {
        ZStack {
            Color.blue.edgesIgnoringSafeArea(.all)
            VStack(spacing: 40) {
                Spacer()
                RoundedStar(cornerRadius: radius)
                    .aspectRatio(1, contentMode: .fit)
                    .foregroundColor(.yellow)
                    .overlay(Text("     cornerRadius: \(Int(self.radius))     ").font(.body))
                HStack {
                    ForEach([0, 10, 20, 40, 80, 200], id: \.self) { value in
                        Button(String(value)) {
                            withAnimation(.easeInOut(duration: 0.3)) {
                                self.radius = CGFloat(value)
                            }
                        }
                        .frame(width: 50, height: 50)
                        .foregroundColor(.black)
                        .background(Color.yellow.cornerRadius(8))
                    }
                }
                Spacer()
            }
        }
    }
}

Laufen in Swift Playgrounds auf dem iPad

Dies läuft wunderbar auf einem iPad in der Swift Playgrounds App. Einfach hinzufügen:

import PlaygroundSupport

oben und

PlaygroundPage.current.setLiveView(ContentView())

Am Ende.


Verwenden der RoundedStar-Form zum Erstellen der EU-Flagge

struct ContentView: View {
    let radius: CGFloat = 100
    let starWidth: CGFloat = 36
    let numStars = 12
    
    var body: some View {
        ZStack {
            Color.blue
            ForEach(0..<numStars) { n in
                RoundedStar(cornerRadius: 0)
                    .frame(width: starWidth, height: starWidth)
                    .offset(x: radius * cos(CGFloat(n) / CGFloat(numStars) * 2 * .pi), y: radius * sin(CGFloat(n) / CGFloat(numStars) * 2 * .pi))
                    .foregroundColor(.yellow)
            }
        }
    }
}