SwiftUI Просмотр структуры без перезагрузки - PullRequest
3 голосов
/ 29 октября 2019

Я хотел бы создать звездное фоновое представление в 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)
    }
}

A 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 как static let из BackgroundView, но это довольно громоздко. Каков наилучший способ иметь представление, зависящее от случайных значений (позиций), определять эти позиции только один раз?


Кроме того, рендеринг довольно медленный. Я попытался вызвать .drawingGroup() на ForEach, но это, кажется, мешает интерполяции непрозрачности анимации. Есть ли какой-либо жизнеспособный способ ускорить создание / повторную визуализацию вида с множеством Circle() элементов?

1 Ответ

2 голосов
/ 29 октября 2019

Медлительность, возникающая из-за настройки слишком сложной анимации в 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
          // }
        }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...