Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Crossfading Images in SwiftUI doesn't seem to work

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?

like image 426
Chris Avatar asked Mar 11 '20 01:03

Chris


2 Answers

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
            }
        }
    }

}
like image 59
Hrabovskyi Oleksandr Avatar answered Oct 10 '22 00:10

Hrabovskyi Oleksandr


This is interesting and it got me thinking.

You can use a flag, say show. And a ZStack that holds your image object. And then use transition combined with animation you want in that image.

But, only do that if show is true.

Attach the .onReceive event to the ZStack instead of your image. In that event, toggle the flag show.

@State private var index = 0
@State private var show = true

private let images = (1...8).map { Image("background\($0)") }
private let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()

var body: some View {
    ZStack {
        if self.show {
            images[self.index]
                .transition(AnyTransition.opacity.animation(.easeInOut(duration: 1.0)))
        }
    }.onReceive(timer) { _ in
        print(self.index)
        self.index += 1
        self.show.toggle()
        if self.index > 4 { self.index = 0 }

    }
}

Voila!

enter image description here


Also, as far as I know, Color is not a View.

A Color is a late-binding token: SwiftUI only resolves it to a concrete value just before using it in a given environment.

ref: https://developer.apple.com/documentation/swiftui/color

like image 4
Glenn Posadas Avatar answered Oct 09 '22 22:10

Glenn Posadas