Swiftui: Unable to Animate Images

SwiftUI: Unable to animate images

Here is possible approach. I simplified it for a demo purpose and less posting code, but idea should be clear and easy transferrable to your real code

Result demo (really it is much fluent than on gif):

demo

Modified model

enum SpeakerSymbol: Int, CaseIterable { // Inherited from Int for convenient below

case speakerEmpty, speaker1, speaker2, speaker3

var image: some View {
var name: String
switch self {
case .speakerEmpty: name = "speaker.slash.fill"
case .speaker1: name = "speaker.1.fill"
case .speaker2: name = "speaker.2.fill"
case .speaker3: name = "speaker.3.fill"
}
return Image(systemName: name).font(.largeTitle)
}
}

Animatable modifier for SpeakerSymbol, required to let SwiftUI animation know that changed SpeakerSymbol value is able to animate

struct SpeakerModifier: AnimatableModifier {
var symbol: SpeakerSymbol

init(symbol: SpeakerSymbol) {
self.symbol = symbol
self.animating = Double(symbol.rawValue) // enum to Double
}

private var animating: Double // Double supports Animatable
var animatableData: Double { // required part of Animatable protocol
get { animating }
set { animating = newValue }
}

func body(content: Content) -> some View {
return SpeakerSymbol(rawValue: Int(animating))!.image // Double -> enum
}
}

Demo of usage

struct TestSpeakerModifier: View {
@State private var speaker: SpeakerSymbol = .speakerEmpty

var body: some View {
VStack {
Color.clear // << just holder area
.modifier(SpeakerModifier(symbol: speaker))
.frame(width: 60, height: 60, alignment: .leading)
Divider()
Button("Toggle") {
withAnimation { // animates between enum states
self.speaker =
(self.speaker == .speakerEmpty ? .speaker3 : .speakerEmpty)
}
}
}
}
}

SwiftUI Can't Animate Image Transitions

The current approach with SwiftUI is to use .transition(), because .animation() is being deprecated.

What is important to understand is that .transition() is triggered when a view appears or disappears. Your view will not be completely re-drawn just because you change a @State variable: in your code, the Image changes but it always stays in the view.

One solution is to trigger the image to completely disappear and make a new one re-appear. The code below does that, depending on the state of showAnimation. See that I only used .transition(), but for a nice effect:

  • it is asymmetric
  • the withAnimation() closure wraps also the changing of the currentIndex
struct Example: View {
let photos = ["gear", "person", "person.2", "car", "leaf"]

@State private var currentIndex: Int = 0
@State private var showAnimation: Bool = true
private var scalingFactor: CGFloat = 0.5

var body: some View {

VStack {
Text("Image Detection")
.font(.system(size: 30))
.fontWeight(.heavy)
.padding(.top, 30)

Spacer()

if showAnimation {
image
} else {
image
}

Spacer()

HStack {
Button(action: {
withAnimation {
showAnimation.toggle()
if self.currentIndex >= self.photos.count {
self.currentIndex = self.currentIndex - 1
} else {
self.currentIndex = 0
}
}
}, label: {
Image(systemName: "arrowtriangle.backward.fill")
})
.padding()
.foregroundColor(Color.blue)
.font(.largeTitle)

Spacer()

Button(action: {
withAnimation {
showAnimation.toggle()
if self.currentIndex < self.photos.count - 1 {
self.currentIndex = self.currentIndex + 1
} else {
self.currentIndex = self.photos.count - 1
}
}
}, label: {
Image(systemName: "arrowtriangle.forward.fill")
})
.padding()
.foregroundColor(Color.blue)
.font(.largeTitle)

}
.padding(.horizontal, 50)

Spacer()
}//v
}//body

private var image: some View {
Image(systemName: photos[currentIndex])
.resizable()
.frame(width: 250, height: 250)
.transition(.asymmetric(insertion: .move(edge: .trailing), removal: .move(edge: .leading)))
}
}

Transition animation not working in SwiftUI

The problem is that when views come and go in a ZStack, their "zIndex" doesn't stay the same. What is happening is that the when "showMessage" goes from true to false, the VStack with the "Hello World" text is put at the bottom of the stack and the yellow color is immediately drawn over top of it. It is actually fading out but it's doing so behind the yellow color so you can't see it.

To fix it you need to explicitly specify the "zIndex" for each view in the stack so they always stay the same - like so:

struct ContentView: View {
@State private var showMessage = false

var body: some View {
ZStack {
Color.yellow.zIndex(0)

VStack {
Spacer()
Button(action: {
withAnimation(.easeOut(duration: 3)) {
self.showMessage.toggle()
}
}) {
Text("SHOW MESSAGE")
}
}.zIndex(1)

if showMessage {
Text("HELLO WORLD!")
.transition(.opacity)
.zIndex(2)
}
}
}
}

Animation of image through layoutIfNeeded stopped working

I finally found a solution by adding a override func viewDidAppear to the class and then adding the animation to that function. I guess all the images need to be properly loaded and placed before they can be animated or something like that?

How to animate a sequence of images using SwiftUI?

The accepted answer works very well, with the unfortunate issues mentioned by bhagyash ingale which makes it very hard to use. It would be useful if the specific methods of Image could be reused via protocols or something. I have a very poor and maybe huge cannon for a fly solution for this, maybe it'll be easier in time but for now...

class LoadingTimer {

let publisher = Timer.publish(every: 0.1, on: .main, in: .default)
private var timerCancellable: Cancellable?

func start() {
self.timerCancellable = publisher.connect()
}

func cancel() {
self.timerCancellable?.cancel()
}
}
struct LoadingView: View {

@State private var index = 0

private let images = (0...7).map { UIImage(named: "Image-\($0).jpg")! }
private var timer = LoadingTimer()

var body: some View {

return Image(uiImage: images[index])
.resizable()
.frame(width: 100, height: 100, alignment: .center)
.onReceive(
timer.publisher,
perform: { _ in
self.index = self.index + 1
if self.index >= 7 { self.index = 0 }
}
)
.onAppear { self.timer.start() }
.onDisappear { self.timer.cancel() }
}
}

I don't like this but it gets the job done and relies on a Image.



Related Topics



Leave a reply



Submit