New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SwiftUI: Overwrite KFImage appear animation #1806
base: master
Are you sure you want to change the base?
Conversation
Thanks for this. I didn't know the |
@alexeichhorn With the change here, I found the List Demo scene in the sample app works in a quite funny way: Simulator.Screen.Recording.-.iPhone.12.-.2021-09-21.at.11.02.13.mp4When I move it out from the transaction block, it works back to normal. Is that necessary or is it trying to fix a certain problem? |
Seems like I have only tested in the sample app with the first commit. Sorry for that.
It should actually prevent these weird animations 😂, but in a different scenario. In your sample apps you don't really use the
So setting loadedImage inside the transaction block is to prevent that. If you move it out, I can give you few examples where it completely breaks animations:
(adding And this is how it looks like: RPReplay_Final1632440733.MP4
And it will look like this: RPReplay_Final1632440756.MP4And in a similar way you can theoretically mess up most of these examples by just adding the |
I am working on a solution for this - it's a bit harder than anticipated. I currently almost have a solution, but the last Transition example in the Demo App is not 100% working. Everything else works. |
@alexeichhorn |
Any updates? |
Still having problems to handle the animation correctly when also applying a transition animation. As @onevcat said, SwiftUI animations are fragile. Fixing one thing, breaks another. Fixing such little UI bugs in SwiftUI is very hard as you don't have full control. So I am sorry, I don't have the mental endurance to fix it currently 😂. Only solution that would fully work is basically just using the UIKit version and wrapping it for SwiftUI. Might however not be the best solution for other reasons. |
I will revisit this issue this weekend when I can find some more time! |
Hi, I have a chance to check this issue again today, and here are some of my thoughts.
+ @State private var shown = false
List(objects) { object in
HStack {
KFImage(object.url)
.resizable()
- .animation(nil)
Text(object.title)
}
}
- .animation(.easeInOut)
+ .animation(.easeInOut, value: shown)
+ .opacity(shown ? 0.0 : 1.0)
+ .onAppear { shown.toggle() }
|
Problem:
With pull request #1767 you fixed some animation issues, but also created a new one (at least for my use case). When you have a KFImage view with an animation modifier somewhere in the tree, this animation is also applied when the image was loaded. In ImageBinder a
withAnimation
-block is used to animate the appearance when needed, but awithAnimation
-block doesn't overwrite any animation set in any parent of KFImage. This leads especially to weird animations when the image is loaded from cache and therefore no animation should happen, which happens anyway.Also ImageBinder sets
loadedImage
directly which also often causes scaling animations when theresizable()
modifier is applied to KFImage.Failed solution: This seems like the trivial solution, but doesn't quite work: Set the animation modifier with
animation(nil)
like this:This fixes the problem of having unwanted animations when the image inside KFImage is loaded, but also destroys the animation when e.g. two objects in the list get switched. In this example, the text would animate as expected in SwiftUI, but the image would just switch instantly. So this would completely disable the animation behaviour of these images (a wrapper with animation enabled doesn't help there).
Proposed Solution:
Instead of using a
withAnimation
-block in ImageBinder when the image was successfully loaded, I used a transaction block as follows:By using the
disableAnimations
property, you can actually overwrite the animation set on the view and have a guarantee to use the given one. The naming ofdisableAnimations
is actually a bit confusing, because it only disables animations defined in the view hierarchy. I guess a better name would beoverwriteAnimations
. Therefore when using e.g.animation = nil
, it is guaranteed there won't be any animation, whilewithAnimation
doesn't do that. I also included setting theloadedImage
inside the block, which prevents the weird scaling animations when the image is loaded.Things to consider:
Developers may want to have custom animations when the image is loaded, which would only be possible via the internal
ImageTransition
, which currently only supports a linear fade, after this request and animation modifiers wouldn't affect any animations initiated by Kingfisher itself. So it would enable having different animations for image appearance and anything else in your app.I think this way of handling animations is a better approach, but maybe you have a different taste or a better idea to solve this problem.