如何在 SwiftUI 中的异步操作期间显示 ProgressView

How to display ProgressView during an async operation in SwiftUI

您好! 我想在我的操作过程中显示进度。我是 swift 和 swiftui 的新手。请帮忙... 这是带有一种异步方法的模型

class CountriesViewModel : ObservableObject {
    
    @Published var countries: [String] = [String]()
    @Published var isFetching: Bool = false
    
    @MainActor
    func fetch() async {
        
        countries.removeAll()
        
        isFetching = true
        
        countries.append("Country 1")
        Thread.sleep(forTimeInterval: 1)
        countries.append("Country 2")
        Thread.sleep(forTimeInterval: 1)
        countries.append("Country 3")
        Thread.sleep(forTimeInterval: 1)
        
        isFetching = false
    }
}

ContentView:

struct ContentView: View {
    
    @StateObject var countriesVM = CountriesViewModel()
    
    var body: some View {
        ZStack {
            if (countriesVM.isFetching) {
                ProgressView("Fetching...")
            }
            else {
                List {
                    ForEach(countriesVM.countries, id: \.self) { country in
                        Text(country)
                    }
                }
                .task {
                    await countriesVM.fetch()
                }
                .refreshable {
                    Task {
                        await countriesVM.fetch()
                    }
                }
            }
        }
    }
}

不显示进度。我做错了什么?

您可以试试这个简单的方法:

struct ContentView: View {
    @StateObject var countriesVM = CountriesViewModel()
    
    var body: some View {
        ZStack {
            if (countriesVM.isFetching) {
                ProgressView("Fetching...")
            }
            else {
                List {
                    ForEach(countriesVM.countries, id: \.self) { country in
                        Text(country)
                    }
                }
                .refreshable {
                    Task {
                        await countriesVM.fetch()
                    }
                }
            }
        }
        .task {  // <-- here
            await countriesVM.fetch()
        }
    }
    
    class CountriesViewModel : ObservableObject {
        
        @Published var countries: [String] = [String]()
        @Published var isFetching: Bool = true  // <-- here
        
        @MainActor
        func fetch() async {
            
            countries.removeAll()
            
            isFetching = true
            
            countries.append("Country 1")
            Thread.sleep(forTimeInterval: 1)
            countries.append("Country 2")
            Thread.sleep(forTimeInterval: 1)
            countries.append("Country 3")
            Thread.sleep(forTimeInterval: 1)
            
            isFetching = false
        }
    }
}

EDIT-1:包括一个刷新数据的按钮。

struct ContentView: View {
    @StateObject var countriesVM = CountriesViewModel()
    
    var body: some View {
        
        ZStack {
            if countriesVM.isFetching {
                ProgressView("Fetching...")
            }
            else {
                VStack {
                    Button("refresh", action: {
                        Task { await countriesVM.fetch() }
                    }).buttonStyle(.bordered)
                    List {
                        ForEach(countriesVM.countries, id: \.self) { country in
                            Text(country)
                        }
                    }
                    .refreshable {
                        await countriesVM.fetch()
                    }
                }
            }
        }
        .task {
            await countriesVM.fetch()
        }
    }
    
    class CountriesViewModel : ObservableObject {
        
        @Published var countries: [String] = [String]()
        @Published var isFetching: Bool = false
        
        @MainActor
        func fetch() async {
            countries.removeAll()
            
            isFetching = true
            
            // representative background async process
            DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 2) {
                // eventualy, updates on the main thread for the UI to use
                DispatchQueue.main.async {
                    self.countries.append("Country 1")
                    self.countries.append("Country 2")
                    self.countries.append("Country 3")
                    self.isFetching = false
                }
            }
        }
    }
}