0

I'm still new to SwiftUI. I'm trying to get each change of an image to start out at opacity 0.0 (fully transparent), then increase to opacity 1.0 (fully opaque) I expected I could achieve this using the .opacity transition. .opacity is described as a "transition from transparent to opaque on insertion", so my assumption is that by stating "withAnimation" in my Button action, I'd trigger the Image to be re-rendered, and the transition would occur beginning from faded to transparent. Instead I see the same instant appear of the new shape & slow morphing to a new size, no apparent change in .opacity. Code and .gif showing current result, below. I've used UIKit & know I'd set alpha to zero, then UIView.animate to alpha 1.0 over a duration of 1.0, but am unsure how to get the same effect in SwiftUI. Thanks!

enter image description here

struct ContentView: View {
    @State var imageName = ""
    var imageNames = ["applelogo", "peacesign", "heart", "lightbulb"]
    @State var currentImage = -1
    
    var body: some View {
        VStack {
            
            Spacer()

            Image(systemName: imageName)
                .resizable()
                .scaledToFit()
                .padding()
                .transition(.opacity)
            
            Spacer()
            
            Button("Press Me") {
                currentImage = (currentImage == imageNames.count - 1 ? 0 : currentImage + 1)
                withAnimation(.linear(duration: 1.0)) {
                    imageName = imageNames[currentImage]
                }
            }
            
        }
    }
}
Gallaugher
  • 1,593
  • 16
  • 27

2 Answers2

2

The reason you are not getting the opacity transition is that you are keeping the same view. Even though it is drawing a different image each time, SwiftUI sees Image as the same. the fix is simple: add .id(). For example:

        Image(systemName: imageName)
            .resizable()
            .scaledToFit()
            .padding()
            .transition(.opacity)
            // Add the id here
            .id(imageName)
Yrb
  • 8,103
  • 2
  • 14
  • 44
  • Thanks so much. This is getting closer. This seems to .transition the displayed image out using .opacity. On click I'd like to eliminate the current image and "fade in" the new image. – Gallaugher May 24 '22 at 20:38
  • That is actually a different question than the one asked. This is the transition you wrote. When you ask the new question, be very specific on what you want the transition to do. – Yrb May 24 '22 at 20:57
  • It seems the reason I'm not seeing the behavior you'd mentioned above is, at least with Xcode 13.4, the initial transition (what would be the insertion transition in an asymmetric transition) does not show up in preview mode, but it does show up in the simulator. Not using asymm trans, but insertions weren't showing. – Gallaugher May 24 '22 at 22:14
  • You should always test up through a real device if things aren't working like they should. The simulators are just that. That being said, there are bugs with certain devices that are reflected in the simulators... – Yrb May 24 '22 at 22:20
  • @Gallaugher: Using the `.id` modifier without knowing the real use case of it and using it because it seems solve an issue in a certain platform is very dangerous approach, we should use a code that we know why it works, and just avoid using any simple and short cut code because it seems works. – ios coder May 24 '22 at 23:33
0

Here is the correct approach for this kind of issue:

We should not forgot how transition works!!! Transition modifier simply transmit a view to nothing or nothing to a view (This should be written with golden ink)! in your code there is no transition happening instead update happing.

struct ContentView: View {
    
    @State var imageName1: String? = nil
    @State var imageName2: String? = nil
    
    var imageNames: [String] = ["applelogo", "peacesign", "heart", "lightbulb"]
    @State var currentImage = -1
    
    var body: some View {
        VStack {
            
            Spacer()
            
            ZStack {
                
                if let unwrappedImageName: String = imageName1 {
                    
                    Image(systemName: unwrappedImageName)
                        .resizable()
                        .transition(AnyTransition.opacity)
                        .animation(nil, value: unwrappedImageName)
                        .scaledToFit()
                    
                }
                
                if let unwrappedImageName: String = imageName2 {
                    
                    Image(systemName: unwrappedImageName)
                        .resizable()
                        .transition(AnyTransition.opacity)
                        .animation(nil, value: unwrappedImageName)
                        .scaledToFit()
                    
                }
                
            }
            .padding()
            .animation(.linear, value: [imageName1, imageName2])
            
            Spacer()
            
            Button("Press Me") {
                currentImage = (currentImage == imageNames.count - 1 ? 0 : currentImage + 1)
                
                if imageName1 == nil {
                    imageName2 = nil; imageName1 = imageNames[currentImage]
                }
                else {
                    imageName1 = nil; imageName2 = imageNames[currentImage]
                }
                
            }
            
        }
        
    }
}
ios coder
  • 1
  • 4
  • 31
  • 91