I would like to create a starry background view in SwiftUI that has its stars located randomly using Double.random()
, but does not reinitialise them and move them when the parent view reloads its var body
.
struct ContentView: View {
@State private var showButton = true
var body: some View {
ZStack {
BackgroundView()
if showButton {
Button("Tap me"){
self.showButton = false
}
}
}
}
}
I define my background view as such.
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
is defined as
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
}
}
}
}
As one can see, a Star
heavily relies on random values, for both its animation (to create a 'random' twinkling effect) as well as its position. When the parent view of the BackgroundView
, ContentView
in this example, gets redrawn however, all Star
s get reinitialised, their position values change and they move across the screen. How can this best be prevented?
I have tried several approaches to prevent the positions from being reinitialised. I can create a struct StarCollection
as a static let
of BackgroundView
, but this is quite cumbersome. What is the best way to go about having a View dependent on random values (positions), only determine those positions once?
Furthermore, the rendering is quite slow. I have attempted to call .drawingGroup()
on the ForEach
, but this then seems to interfere with the animation's opacity interpolation. Is there any viable way to speed up the creation / re-rendering of a view with many Circle()
elements?
Aucun commentaire:
Enregistrer un commentaire