Adding a placeholder to AsyncImage and showing a different image if AsyncImage fails to get the image

2.9k views Asked by At

I'm trying to use an AsyncImage to show a ProgressView while it gets an image from a URL, but if it fails to get the image then a different image should be displayed. My code is working in the sense that it will show the different image if the AsyncImage fails to get an image, but I can't get a placeholder ProgressView to show while the async operation is running. The code below does not contain the placeholder modifier and works OK (EDIT: Actually unconfirmed if it's working...):

var body: some View {
    AsyncImage(url: url, scale: scale ?? 1) { phase in
        if let image = phase.image {
            image
                .resizable()
                .aspectRatio(contentMode: contentMode ?? .fit)
                .frame(width: width, height: height)
        } else {
            Image("placeholder_img")
                .resizable()
                .frame(width: 50, height: 50)
                .border(Color(.NeutralPalette5))
                .padding()
                .aspectRatio(contentMode: .fill)
        }
    }
    .frame(width: width, height: height)
}

But adding a placeholder modifier to this code does not work:

var body: some View {
    AsyncImage(url: url, scale: scale ?? 1) { phase in
        if let image = phase.image {
            image
                .resizable()
                .aspectRatio(contentMode: contentMode ?? .fit)
                .frame(width: width, height: height)
        } else {
            Image("placeholder_img")
                .resizable()
                .frame(width: 50, height: 50)
                .border(Color(.NeutralPalette5))
                .padding()
                .aspectRatio(contentMode: .fill)
        }
    } placeholder: {
        ProgressView()
            .progressViewStyle(.circular)
    }
    .frame(width: width, height: height)
}

The above results in the following error:

Failed to produce diagnostic for expression; please submit a bug report (https://swift.org/contributing/#reporting-bugs) and include the project

Can anyone tell me how I can use a placeholder with my code? I'm basically trying to show a ProgressView while the AsyncImage gets the image, but if it fails to get the image then a different image should be displayed, so if there's a better way to do that please let me know.

1

There are 1 answers

0
workingdog support Ukraine On BEST ANSWER

When you use AsyncImage(url: ...) with a placeholder: {..} you have the image, not the phase.

 var body: some View {
     AsyncImage(url: url) { image in  // <-- here
         image.resizable()
     } placeholder: {
         ProgressView().progressViewStyle(.circular)
     }
 }

To present a different image, presumably after a certain time has passed, you will have to create the code yourself. Or use the following approach with the phase:

 AsyncImage(url: url) { phase in
     if let image = phase.image {
         image.resizable() // Displays the loaded image.
     } else if phase.error != nil {
         Image("Goofy") // Indicates an error, show default image
     } else {
         // Acts as a placeholder.
         ProgressView().progressViewStyle(.circular)
         // Image("Mickey Mouse")
     }
 }