How to Create a SwiftUI RoundedStar Shape?

一笑奈何 提交于 2021-02-10 06:10:18

问题


This is a self-answered question which is perfectly acceptable (and even encouraged) on Stack Overflow. The point is to share something useful to others.

SwiftUI has a RoundedRectangle Shape. It would be nice to have a five-pointed star with rounded tips that could be used for filling, clipping, and animation.

This Stack Overflow answer shows how to make a RoundedStar as a custom UIView using UIBezierPath.

How can this code be adapted to SwiftUI as a Shape that can be animated?


回答1:


Here is the RoundedStar code adapted as an animatable SwiftUI 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
    }
}

The code is very similar to the UIBezierPath version except that it uses the new Angle type which provides easy access to both degrees and radians. The code to draw the star rotated was removed because it is easy to add rotation to a SwiftUI shape with the .rotationEffect(angle:) view modifier.


Demonstration:

Here is a demonstration that show the animatable qualities of the cornerRadius setting as well as showing what the various cornerRadius settings look like on a full-screen star.

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()
            }
        }
    }
}

Running in Swift Playgrounds on iPad

This runs beautifully on an iPad in the Swift Playgrounds app. Just add:

import PlaygroundSupport

at the top and

PlaygroundPage.current.setLiveView(ContentView())

at the end.


Using the RoundedStar shape to create EU Flag

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)
            }
        }
    }
}


来源:https://stackoverflow.com/questions/63650216/how-to-create-a-swiftui-roundedstar-shape

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!