ProgressView 在 SwiftUI 中数据解析期间未更新

问题描述 投票:0回答:1

很难重现完整的代码,但我会尽力清楚地解释这个问题。

我试图显示一个进度条视图来显示解析在线下载的一些数据的状态。我用于解析的函数正确地在控制台中打印下载百分比,但由于某种原因,我的进度视图没有更新。

downloadData 函数有一个转义闭包来报告下载的阶段(downloadData、parseData、savingData)。

下载数据时,ProgressView 正确更新。但是,当 downloadStage 处于 parseData 阶段时,视图中不会更新任何内容。我看到 ProgressView 显示“正在解析数据”,但该值没有改变。控制台打印数据,因此我确信数据已正确传递到视图中。

可能是什么问题?

我注意到,如果我在代码中插入一点延迟,ProgressView 就会更新。如何确保视图正确更新?


  // MARK: - Download Data
    func downloadData(lista: PlayListModel, completion: @escaping (_ stage: DownloadStage, _ progress: Double, _ total: Int) -> Void) async throws {
        
        
        do {
            var m3uString = ""
            // RUN as 1st
            m3uString = try await requestM3UListAlamo(from: lista.playlistUrl, progressHandler: { progress in
                
                completion(.downloadData, progress, 100)
                
            })
            
            completion(.parseData, 0, 0)
// At this point func don't update the UI 
            let group = try await sdparsePlaylist(m3uContent: m3uString) { progress, total in
                completion(.parseData, progress, Int(total))
            }
            
            completion(.savingData, 0.0, 0)
            
            try await saveData(lista: lista, grouped: group) { stage, result in
                
                completion(stage, 100, 100)

            }
            
            
        } catch {
            
            // Handle failure and update completion stage
            print("Download failed with error: \(error.localizedDescription)")
            completion(.fail, 0, 0)
            
            throw error // Propagate error to caller
        }
    }
    

  func sdparsePlaylist(m3uContent: String, completion: @escaping (_ progress: Double, _ total: Int) -> Void) async throws -> [String: [PlaylistItem]] {
        print("4-------------- parsing playlist --------------")
        var appoggio: [String: [PlaylistItem]] = [:]
        let createDate = Date()

        do {
            let playlist = try await self.parser.parse(m3uContent)
            let media = playlist.medias
            let totalItems = media.count
            var progress = 0
            
            for item in media {
                let attributes = item.attributes
                let groupTitle = attributes.groupTitle ?? "NOT CLASSIFIED"
                let playlistItem = PlaylistItem(duration: item.duration, tvgId: attributes.id, tvgName: attributes.name, tvgCountry: attributes.country, tvgLanguage: attributes.language, tvgLogo: attributes.logo, tvgChno: attributes.channelNumber, tvgShift: attributes.shift, groupTitle: groupTitle, seasonNumber: attributes.seasonNumber, episodeNumber: attributes.episodeNumber, kind: item.kind.rawValue, url: item.url, lastPlay: createDate)
                
                if var existingPlaylist = appoggio[groupTitle] {
                    existingPlaylist.append(playlistItem)
                    appoggio[groupTitle] = existingPlaylist
                } else {
                    appoggio[groupTitle] = [playlistItem]
                }
                
                
                let currentProgress = Double(progress) / Double(totalItems) * 100
                completion(currentProgress, 100)
                print("parser background: \(currentProgress)% (\(progress + 1) / \(totalItems))")
                progress += 1
                
            }
        } catch {
            print(error.localizedDescription)
            throw error
        }
       
        return appoggio
    }

景色:

import SwiftUI
import SwiftData
import Alamofire

struct AddNewPlaylist: View {
    @Environment(\.modelContext) private var modelContext
    @State private var playlistName = "TV"
@State private var playlistUrl = "https://country.m3u"

    
    @State var downloadStage: DownloadStage = .none
    @State var progress: CGFloat = 0.5
    @State var total = 0
    @Binding var isPresented: Bool
    @State var bounce = false
    @State private var showAlert = false
    @State private var alertMessage = ""
    let backgroundParser: BackgroundParser
    
    var body: some View {
        VStack {
            Text("Add New Playlist")
                .foregroundStyle(Color("Text1"))
                .font(.title)
                .padding()
            
            Spacer()
            
            TextField("Playlist Name", text: $playlistName)
                .padding()
                .background(RoundedRectangle(cornerRadius: 10).stroke(Color.white, lineWidth: 1))
                .foregroundStyle(Color("Text1"))
            TextField("M3U Link", text: $playlistUrl)
                .padding()
                .background(RoundedRectangle(cornerRadius: 10).stroke(Color.white, lineWidth: 1))
                .foregroundStyle(Color("Text1"))
                .overlay {
                    HStack {
                        Spacer()
                        if !playlistUrl.isEmpty {
                            Button(action: {
                                self.playlistUrl = "" // Clear the text
                            }) {
                                Image(systemName: "multiply.circle.fill")
                                    .foregroundColor(.gray)
                            }
                            .padding(.trailing, 8)
                        }
                    }
                }
            HStack {
                Button {
                    Task {
                        await backgroundParser.cancelDownload()
                        isPresented = false
                    }
                } label: {
                    Text("Return")
                }
                .buttonStyle(BorderedButtonStyle())
                .foregroundStyle(Color("Text1"))
                
                Spacer()
                
                Button("Save") {
                    if playlistName != "" && playlistUrl != "" {
                        Task {
                            do {
                                let playlist = PlayListModel(timestamp: Date(), playlistName: playlistName, playlistUrl: playlistUrl, lastUpdate: Date())
                                try await backgroundParser.downloadData(lista: playlist) { stage, prog, total in
                                    // print correcctly but the view dont update
                                    print("going tp update \(stage.description), \(prog), \(total)")
                                    self.downloadStage = stage
                                    self.progress = prog
                                    self.total = total
                                    
                                    if stage == .done {
                                        isPresented = false
                                    }
                                    
                                }
                            } catch {
                                    alertMessage = "Error: \(error.localizedDescription)"
                                    showAlert = true
                            }
                        }
                    }
                }
                .buttonStyle(BorderedButtonStyle())
                .foregroundStyle(Color("Text1"))
                .alert(isPresented: $showAlert) {
                    Alert(title: Text("Error"), message: Text(alertMessage), dismissButton: .default(Text("OK")))
                }
            }
            .padding(.top, 50)
            
            Spacer()
            
            
            VStack{
                switch downloadStage {
                    
                case .downloadData:
                
                    ProgressView(value: progress, total: CGFloat(total)) {
                        HStack {
                            Text("Downloading Data...").font(.title3)
                            Text("\(Int(progress)) / \(total)")
                        }
                    }.progressViewStyle(.linear)
                    
                case .parseData:
                    // Here no work --------------------------------------
                    ProgressView(value: progress, total: 100) {
                        HStack {
                            Text("Parsing Data...").font(.title3)
                            Text("\(Int(progress)) / \(total)")
                        }
                    }.progressViewStyle(.linear)
                
                case .savingData:
                        HStack {
                            Text("Saving data...don't close window").font(.title3)
                           
                        }
                default:
                    
                    Text("")
                }
            }.foregroundColor(.white)
            
            
            Spacer()
        }
        .padding()
        .background {
            LinearGradient(colors: [.color1, .color2, .color3], startPoint: .bottom, endPoint: .topTrailing)
                .ignoresSafeArea()
        }
    }
}


swift swiftui concurrency state
1个回答
0
投票

您的

for item in media
循环不包含任何异步调用 (
await
),这意味着它是同步的,并且没有可以更新 UI 的暂停点。尝试在循环中添加
await Task.yield()

© www.soinside.com 2019 - 2024. All rights reserved.