我想在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
元素的视图的创建/重新渲染?
答案 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
// }
}