无需重新加载的SwiftUI View结构

时间:2019-10-29 14:56:16

标签: swift random swiftui

我想在SwiftUI中创建一个繁星点点的背景视图,该视图使用Double.random()随机定位星星,但是在父视图重新加载其var body时不重新初始化它们并移动它们。

struct ContentView: View {
    @State private var showButton = true

    var body: some View {
        ZStack {
            BackgroundView()
            if showButton {
                Button("Tap me"){
                    self.showButton = false
                }
            }
        }
    }
}

我是这样定义背景视图的。

struct BackgroundView: View {
    var body: some View {
        ZStack {
            GeometryReader { geometry in
                Color.black
                ForEach(0..<self.getStarAmount(using: geometry), id: \.self){ _ in
                    Star(using: geometry)
                }
                LinearGradient(gradient: Gradient(colors: [.purple, .clear]), startPoint: .bottom, endPoint: .top)
                    .opacity(0.7)
            }
        }
    }

    func getStarAmount(using geometry: GeometryProxy) -> Int {
        return Int(geometry.size.width*geometry.size.height/100)
    }
}

Star被定义为

struct Star: View {
    let pos: CGPoint
    @State private var opacity = Double.random(in: 0.05..<0.4)

    init(using geometry: GeometryProxy) {
        self.pos = CGPoint(x: Double.random(in: 0..<Double(geometry.size.width)), y: Double.random(in: 0..<Double(geometry.size.height)))
    }



    var body: some View {
        Circle()
            .foregroundColor(.white)
            .frame(width: 2, height: 2)
            .scaleEffect(CGFloat(Double.random(in: 0.25...1)))
            .position(pos)
            .opacity(self.opacity)
            .onAppear(){
                withAnimation(Animation.linear(duration: 2).delay(Double.random(in: 0..<6)).repeatForever()){
                    self.opacity = self.opacity+0.5
                }
            }
    }
}

正如人们所看到的,Star的动画(以创建“随机”闪烁效果)及其位置都高度依赖随机值。但是,当重绘BackgroundView(在此示例中为ContentView)的父视图时,所有Star都会被重新初始化,它们的位置值会改变,并且它们会在屏幕上移动。如何最好地预防这种情况?

我尝试了几种方法来防止重新初始化职位。我可以将struct StarCollection的{​​{1}}创建为static let,但这很麻烦。要使View依赖于随机值(位置),只确定一次这些位置,最好的方法是什么?


此外,渲染速度很慢。我试图在BackgroundView上调用.drawingGroup(),但是这似乎会干扰动画的不透明度插值。有没有可行的方法来加快包含许多ForEach元素的视图的创建/重新渲染?

1 个答案:

答案 0 :(得分:2)

onAppear中过于复杂的动画设置导致缓慢,您只需要更改self.opacity状态即可启动动画,因此请移出动画并直接添加到形状中。

   Circle()
        .foregroundColor(.white)
        .frame(width: 2, height: 2)
        .scaleEffect(CGFloat(Double.random(in: 0.25...1)))
        .position(pos)
        .opacity(self.opacity)
        .animation(Animation.linear(duration: 0.2).delay(Double.random(in: 0..<6)).repeatForever())
        .onAppear(){
           // withAnimation{ //(Animation.linear(duration: 2).delay(Double.random(in: 0..<6)).repeatForever()){
                self.opacity = self.opacity+0.5
          // }
        }