如何在SwiftUI中绘制不同大小的同心虚线圆

fdbelqdn  于 2023-09-29  发布在  Swift
关注(0)|答案(2)|浏览(89)

我有下面的视图,它绘制同心圆来填充屏幕:

struct TestView: View {
    var body: some View {
            ZStack {
                Color.red
                ForEach((0...50), id: \.self) { index in
                    
                    Path { path in
                        var radius = CGFloat(120 + (index * 30))
                        let center = (x: UIScreen.main.bounds.size.width/2, y: UIScreen.main.bounds.size.height/2)
                        path.move(to: CGPoint(x: center.x + radius, y: center.y))
                        path.addArc(center: CGPoint(x: center.x, y: center.y), radius: radius, startAngle: Angle(degrees: 0), endAngle: Angle(degrees: 360), clockwise: false)
                    }
                    .stroke(Color.white, style: StrokeStyle(lineWidth: 1, lineCap: .round, lineJoin: .round, dash: [3.0]))
                }
            }
    }
}

渲染时看起来如下所示:

我试图实现的是破折号是圆点,而不是他们的大小间距逐渐增加,因为圆圈向外。希望达到这样的最终结果,但与圆形溢出屏幕边界,同时集中到屏幕上,也尊重安全区,目前我的圈子不是。上半部分的圆弧比下半部分多。任何帮助都是感激不尽的。

htzpubme

htzpubme1#

我建议使用Canvas。然后只需将.ignoresSafeArea涂到安全区域即可。就像这样:

struct Dots: View {
    let innerRadius: CGFloat
    let radiusStep: CGFloat
    let innerDotSize: CGFloat
    let gapRatio: CGFloat

    init(
        innerRadius: CGFloat = 80,
        radiusStep: CGFloat = 45,
        innerDotSize: CGFloat = 8,
        gapRatio: CGFloat = 2.2
    ) {
        self.innerDotSize = innerDotSize
        self.gapRatio = gapRatio
        self.innerRadius = innerRadius
        self.radiusStep = radiusStep
    }

    var body: some View {
        Canvas { context, size in
            let originX = size.width / 2
            let originY = size.height / 2
            let cornerDistance = ((originX * originX) + (originY * originY)).squareRoot()
            let circumference = 2 * CGFloat.pi * innerRadius
            let nDots = Int((circumference / (innerDotSize * (gapRatio + 1))) + 0.5)
            let angleStep = (2.0 * .pi) / Double(nDots)
            var radius = innerRadius
            while radius < cornerDistance {

                // Start at 12 o' clock
                var angle = -Double.pi / 2.0
                let circumference = 2 * CGFloat.pi * radius
                let dotSize = circumference / (CGFloat(nDots) * (1.0 + gapRatio))
                for _ in 0..<nDots {
                    let x = originX + (cos(angle) * radius) - (dotSize / 2)
                    let y = originY + (sin(angle) * radius) - (dotSize / 2)
                    context.fill(
                        Path(ellipseIn: CGRect(x: x, y: y, width: dotSize, height: dotSize)),
                        with: .foreground
                    )
                    angle += angleStep
                }
                radius += radiusStep
            }
        }
    }
}

struct ContentView: View {
    var body: some View {
        Dots()
            .foregroundColor(.gray)
            .ignoresSafeArea()
    }
}

zsbz8rwp

zsbz8rwp2#

使用scaleEffectoffsetrotationEffectForEach中的参数将所有圆放置到位。

ZStack {
    // for each "ring"...
    ForEach(0..<10) { i in 
        // for each circle in the "ring"...
        // it looks like there are 20 circles in each "ring"
        ForEach(0..<20) { j in 
            // try experimenting by changing the constants
let i = CGFloat(i)
            let circleSize: CGFloat = 16
            let scaleStep = 0.3
            // scale and offset depends on the ring number
            let scale = 1 + i * scaleStep
            // add extra offset if you want the circles to be equally spaced
            //let extra = (i * (2 + (i - 1) * scaleStep) * circleSize) / 2
            let offset = 100 + i * 50// + extra
            // angle doesn't depend on the ring number, but depends on which circle it is in the ring
            let angle = Double(j) * (360 / 20)
            Circle()
                .fill(.black)
                .frame(width: circleSize, height: circleSize)
                // scale, move the circle downwards, and finally rotate
                .scaleEffect(scale)
                .offset(y: offset)
                .rotationEffect(.init(degrees: angle))
        }
    }
}

要解决安全区域问题,请将ZStack放在忽略安全区域的GeometryReader中:

GeometryReader { geo in
    ZStack {
        ...
    }.frame(width: geo.size.width, height: geo.size.height)
}.ignoresSafeArea()

相关问题