Home > OS >  How To Listen For New Phase Success When Image Url Changes in AsyncImage SwiftUI
How To Listen For New Phase Success When Image Url Changes in AsyncImage SwiftUI

Time:06-15

I have a simple SwiftUI application that regenerates random background on every tap gesture and adds a quote to it.

AsyncImage(url: URL(string: backgroundImgUrl)) { phase in
            switch phase {
            case .empty:
                Color.purple.opacity(0.1)
            case .success(let image):
                image
                    .ignoresSafeArea()
                    .scaledToFit()
                    .onAppear {
                        //code here happen for 1 time only even for every background change
                    }
                 //can I execute code here??? syntax error happens
            case .failure(_):
                Image(systemName: "exclamationmark.icloud")
                    .resizable()
                    .scaledToFit()
            @unknown default:
                Image(systemName: "exclamationmark.icloud")
            }
        }

The problem when I try to add quotes with a random on-tap gesture it appears before the background gets updated. so I want to execute code when every new random background is generated but I couldn't. can you give me some hints, please?

onTapGesture code:

.onTapGesture {
        backgroundImgUrl = "https://source.unsplash.com/1600x900/?nature&\(Int.random(in: 1..<6236))"
        
    }

CodePudding user response:

you could try something like this using onChange:

struct ContentView: View {
    @State var backgroundImgUrl = "https://source.unsplash.com/1600x900/?nature&\(Int.random(in: 1..<6236))"
    
    var body: some View {
        AsyncImage(url: URL(string: backgroundImgUrl)) { phase in
            switch phase {
            case .empty:
                Color.purple.opacity(0.1)
            case .success(let image):
                image
                    .ignoresSafeArea()
                    .scaledToFit()
                    .onChange(of: image) { _ in   // <-- here
                        // code here happen for every image change
                        print("----> in onChange ")
                    }
            case .failure(_):
                Image(systemName: "exclamationmark.icloud")
                    .resizable()
                    .scaledToFit()
            @unknown default:
                Image(systemName: "exclamationmark.icloud")
            }
        }
        .onTapGesture {
            backgroundImgUrl = "https://source.unsplash.com/1600x900/?nature&\(Int.random(in: 1..<6236))"
        }
    }
}

CodePudding user response:

It is possible by adding identifier, like

case .success(let image):
    image         
        .ignoresSafeArea()
        .scaledToFit()
        .onAppear {
            // this works
        }
        .id(backgroundImgUrl)   // << here !! (tested Xcode 13.4 / iOS 15.5)

Note: as new Image view is inserted and old is removed the default opacity transition effect might be visible. It can be mitigated, if not needed, with adding nil animation.

Test module on GitHub

  • Related