我想在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
创建为static let
的BackgroundView
,但这很麻烦。要使View依赖于随机值(位置),只确定一次这些位置,最好的方法是什么?
此外,渲染速度非常慢。我试图在.drawingGroup()
上调用ForEach
,但是这似乎会干扰动画的不透明度插值。是否有任何可行的方法来加速具有许多Circle()
元素的视图的创建/重新渲染?
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
// }
}