很难重现完整的代码,但我会尽力清楚地解释这个问题。
我试图显示一个进度条视图来显示解析在线下载的一些数据的状态。我用于解析的函数正确地在控制台中打印下载百分比,但由于某种原因,我的进度视图没有更新。
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()
}
}
}
您的
for item in media
循环不包含任何异步调用 (await
),这意味着它是同步的,并且没有可以更新 UI 的暂停点。尝试在循环中添加 await Task.yield()
。