SwiftUI AsyncImage ProgressView deadline

601 Views Asked by At

How can I achieve such a scenario.

  1. Using the AsyncImage I load the url.
  2. Progress view turns around as placeholder
  3. After 2 seconds we don't get an error
  4. Our ProgressView changes to an image

This is a code:

AsyncImage(url: exampleURL) { phase in
    if let image = phase.image {
        image
            .imageModifier()
    } else if phase.error != nil {
        Image("example_image")
            .imageModifier()
    } else {
        ProgressView()
            .font(.largeTitle)
    }
}

There is any short and clean solution of I need to using custom implementation async image and using for example taskgroups? Thanks for any ideas.

1

There are 1 best solutions below

0
workingdog support Ukraine On

You could try this approach, using a Timer with onReceive, works well for me. Set an appropriate timeRemaining. After that time has elapsed, the AsyncImage will present the chosen image ("timer"), if the image is downloaded before that time, that image will be displayed.

struct ContentView: View {
    let url = URL(string: "https://www.trinum.com/ibox/ftpcam/mega_mtgenevre_sommet-des-gondrans.jpg")!

    let timer = Timer.publish(every: 1.0, on: .main, in: .common).autoconnect()
    @State var timeRemaining = 2
    
    var body: some View {
        AsyncImage(url: url) { phase in
            if timeRemaining <= 0 {
                Image(systemName: "timer")
            } else {
                switch phase {
                    case .empty: ProgressView()
                    case .success(let image):
                      image.resizable()
                        .onAppear { timer.upstream.connect().cancel() }
                    case .failure:
                      Image(systemName: "wifi.slash")
                        .onAppear { timer.upstream.connect().cancel() }
                    @unknown default: EmptyView()
                }
            }
        }
        .onReceive(timer) { _ in
            if timeRemaining > 0 {
                timeRemaining -= 1
            } else {
                timer.upstream.connect().cancel()
            }
        }
    }
    
}