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