dmytro-anokhin / url-image

AsyncImage before iOS 15. Lightweight, pure SwiftUI Image view, that displays an image downloaded from URL, with auxiliary views and local cache.
MIT License
1.11k stars 96 forks source link

2.1.0 - changing initial image url shows placeholder until the view's visibility is toggled #96

Closed montehurd closed 4 years ago

montehurd commented 4 years ago

Summary and/or background Changing initial image url requires a view visibility toggle before the newly fetched image appears.

OS and what device you are using

Version of URLImage library

What you expected would happen When you change the initial url, the new image should be shown after it is fetched.

What actually happens The placeholder is shown even though the image has been retrieved. You must manually hide and show the view before the new image will appear.

Sample code Here's a quick sample which reproduces the issue:


import SwiftUI
import URLImage

struct ContentView: View {

    static let fishURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/thumb/6/6e/Redear_sunfish_FWS_1.jpg/277px-Redear_sunfish_FWS_1.jpg")!
    static let horseURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/thumb/9/97/Zaniskari_Horse_in_Ladhak%2C_Jammu_and_kashmir.jpg/320px-Zaniskari_Horse_in_Ladhak%2C_Jammu_and_kashmir.jpg")!

    @State var url = fishURL
    @State var show = true

    private let placeholder: some View = Image(systemName: "photo")
        .foregroundColor(.white)

    var body: some View {
        Text("If you tap 'Show horse' below it shows just the placeholder. If you try to toggle back and forth between the fish and horse the placeholder is always shown for the horse even though the image has been retrieved.")
            .padding()

        if show {
            URLImage(url: url,
                     empty: {
                        placeholder
                     },
                     inProgress: { _ in
                        placeholder
                     },
                     failure: { error, retry in
                        placeholder
                     },
                     content: { image in
                        image
                     }
            )
            .foregroundColor(.white)
            .grayscale(0.3)
            .frame(width: 150, height: 150)
            .clipped()

        }
        Button(action: {
            withAnimation{
                url = url.absoluteString.contains("sunfish") ? ContentView.horseURL : ContentView.fishURL
            }
        }, label: {
            Text("Show \(url.absoluteString.contains("sunfish") ? "horse" : "fish")")
        })

        Text("When the placeholder is visible (important), if you tap 'Hide image' below, and then tap 'Show image', it will only then correctly show the horse and toggling between the two images will work as expected.")
            .padding()

        Button(action: {
            withAnimation{
                self.show.toggle()
            }
        }, label: {
            Text(show == true ? "Hide image" : "Show image")
        })
    }
}
montehurd commented 4 years ago

btw thanks for the great library! it's super useful!

dmytro-anokhin commented 4 years ago

Hey, thanks for reporting and the sample code. I will look into this later today.

dmytro-anokhin commented 4 years ago

URLImage starts loading from onAppear callback. What happens in your example, because the view already presented onAppear won't get called, and therefore loading won't start. To ensure loading starts I added additional trigger to init. This way URLImage will always start loading. I also keep old triggers so it can still cancel/resume on appearance callbacks.

dmytro-anokhin commented 4 years ago

The fix is in 2.1.1

montehurd commented 4 years ago

Oh very nice! Ya I noticed onAppear wasn't being re-triggered - have run into similar issues with my own SwiftUI 😜. Thanks for the quick turn-around!

dmytro-anokhin commented 4 years ago

Cool, thanks for verifying. I'm closing the issue.