I'd like to achieve an an animated background that crossfades a bunch of images in SwiftUI.
I've managed to get an array of Colors to crossfade nicely between them using the following code snippets, but if I replace the array of Colors with an array of Images the same approach doesn't work. The images are replaced, but not animated. Yet both a Color and an Image are of type some View right?
The following works:
struct ImageBackgroundView: View {
@State private var index = 0
private let colors:[Color] = [.red, .blue, .green, .orange, .pink, .black]
private let timer = Timer.publish(every: 5, on: .main, in: .common).autoconnect()
var body: some View {
colors[self.index]
.animation(.easeInOut(duration: 1))
.onReceive(timer) { _ in
print(self.index)
self.index += 1
if self.index > 5 { self.index = 0 }
}
}
but, if I replace the [Color] array with an array of type [Images], as follows, the images are transitioned, but the crossfade doesn't appear to work:
struct ImageBackgroundView: View {
@State private var index = 0
private let images = (1...8).map { Image("background\($0)") }
private let timer = Timer.publish(every: 5, on: .main, in: .common).autoconnect()
var body: some View {
images[self.index]
.animation(.easeInOut(duration: 1))
.onReceive(timer) { _ in
print(self.index)
self.index += 1
if self.index > 5 { self.index = 0 }
}
}
Can anyone shed any light on why this might be the case? Is it just a bug in SwiftUI at the present time?
I achieved a similar effect in a previous app using all sorts of addSubview calls animating the opacity of overlaying views - all sorts of stuff we shouldn't need to fiddle with in this brave new SwiftUI world right?
I changed your example a little and now background images changed with animation. But I can't suggest, why the same things don't work either for Color
or for Image
. So, here is my working example, maybe it will push you in right direction:
struct AnimatedBackground: View {
@State private var index = 0
private let images: [Image] = ["trash", "star", "circle", "circle.fill", "square", "cloud"].map{ Image(systemName: $0) }
private let timer = Timer.publish(every: 3, on: .main, in: .common).autoconnect()
var body: some View {
ZStack {
ForEach(images.indices, id: \.self) { imageIndex in
self.images[imageIndex]
.resizable()
.transition(.opacity)
.opacity(imageIndex == self.index ? 1 : 0)
}
}
.onReceive(timer) { _ in
withAnimation {
self.index = self.index < self.images.count - 1 ? self.index + 1 : 0
}
}
}
}