UI 使用 AlamoFire downloadProgress 时锁定
UI locking up when using AlamoFire downloadProgress
我正在尝试创建下载进度条并在下载完成时同时显示提醒。
对于此任务,我将 AlamoFire 与 SwiftUI 结合使用,因为它使下载变得容易。
但是,当我使用带有 Published 变量的 ProgressView 跟踪进度时,整个 UI 锁定,我不知道如何修复它。
我尝试将 downloadProgress 添加到单独的 DispatchQueue,但我仍然必须从主线程更新 UI 否则 Xcode 会报错。
如何测试附加的示例代码:
- 点击“开始下载”
- 等待 ProgressView 稍微移动一下
- 单击“显示警报”按钮
- 尝试关闭警报,它不会关闭。
如有任何帮助,我将不胜感激。
导入斯威夫特UI
进口 Alamofire
struct ContentView: View {
@StateObject var viewModel: ViewModel = ViewModel()
@State private var showAlert = false
var body: some View {
VStack {
Button("Show Alert") {
showAlert.toggle()
}
Button("Start download") {
viewModel.startDownload()
}
if viewModel.showProgressView {
ProgressView("Downloading…", value: viewModel.downloadProgress, total: 1.0)
.progressViewStyle(.linear)
}
}
.alert(isPresented: $showAlert) {
Alert(
title: Text("Text"),
dismissButton: .cancel()
)
}
}
}
class ViewModel: ObservableObject {
@Published var currentDownload: DownloadRequest? = nil
@Published var downloadProgress: Double = 0.0
@Published var showProgressView: Bool = false
func startDownload() {
print("Function called!")
showProgressView.toggle()
let queue = DispatchQueue(label: "alamofire", qos: .utility)
let destination = DownloadRequest.suggestedDownloadDestination(for: .documentDirectory)
AF.download("https://speed.hetzner.de/10GB.bin", to: destination)
.downloadProgress(queue: queue) { progress in
print(progress.fractionCompleted)
DispatchQueue.main.async {
self.downloadProgress = progress.fractionCompleted
}
}
.response { response in
print(response)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
这里的问题是您实际上是在用更新向 UI 线程发送垃圾邮件,因为 alamofire 经常调用提供给 downloadProgress
的闭包(查看控制台打印)。您需要稍微错开 AF 进度的更新,以便按下按钮关闭警报可以注册(在 Combine 中,这称为去抖动)。我在这里所做的是添加一个小的时间计数器,以便它仅每 1
秒更新一次进度。这些更新之间的时间使 UI 线程可以自由响应点击等
import SwiftUI
import Alamofire
struct ContentView: View {
@StateObject var viewModel: ViewModel = ViewModel()
@State private var showAlert = false
var body: some View {
VStack {
Button("Show Alert") {
showAlert.toggle()
}
Button("Start download") {
viewModel.startDownload()
}
if viewModel.showProgressView {
ProgressView("Downloading…", value: viewModel.downloadProgress, total: 1.0)
.progressViewStyle(.linear)
}
}
.alert(isPresented: $showAlert) {
Alert(
title: Text("Text"),
dismissButton: .cancel()
)
}
}
}
class ViewModel: ObservableObject {
@Published var currentDownload: DownloadRequest? = nil
@Published var downloadProgress: Double = 0.0
@Published var showProgressView: Bool = false
func startDownload() {
print("Function called!")
showProgressView.toggle()
let queue = DispatchQueue(label: "net", qos: .userInitiated)
let destination = DownloadRequest.suggestedDownloadDestination(for: .documentDirectory)
var last = Date()
AF.download("https://speed.hetzner.de/10GB.bin", to: destination)
.downloadProgress(queue:queue) { progress in
print(progress.fractionCompleted)
if Date().timeIntervalSince(last) > 1 {
last = Date()
DispatchQueue.main.async {
self.downloadProgress = progress.fractionCompleted
}
}
}
.response { response in
// print(response)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
我正在尝试创建下载进度条并在下载完成时同时显示提醒。
对于此任务,我将 AlamoFire 与 SwiftUI 结合使用,因为它使下载变得容易。 但是,当我使用带有 Published 变量的 ProgressView 跟踪进度时,整个 UI 锁定,我不知道如何修复它。
我尝试将 downloadProgress 添加到单独的 DispatchQueue,但我仍然必须从主线程更新 UI 否则 Xcode 会报错。
如何测试附加的示例代码:
- 点击“开始下载”
- 等待 ProgressView 稍微移动一下
- 单击“显示警报”按钮
- 尝试关闭警报,它不会关闭。
如有任何帮助,我将不胜感激。
导入斯威夫特UI 进口 Alamofire
struct ContentView: View {
@StateObject var viewModel: ViewModel = ViewModel()
@State private var showAlert = false
var body: some View {
VStack {
Button("Show Alert") {
showAlert.toggle()
}
Button("Start download") {
viewModel.startDownload()
}
if viewModel.showProgressView {
ProgressView("Downloading…", value: viewModel.downloadProgress, total: 1.0)
.progressViewStyle(.linear)
}
}
.alert(isPresented: $showAlert) {
Alert(
title: Text("Text"),
dismissButton: .cancel()
)
}
}
}
class ViewModel: ObservableObject {
@Published var currentDownload: DownloadRequest? = nil
@Published var downloadProgress: Double = 0.0
@Published var showProgressView: Bool = false
func startDownload() {
print("Function called!")
showProgressView.toggle()
let queue = DispatchQueue(label: "alamofire", qos: .utility)
let destination = DownloadRequest.suggestedDownloadDestination(for: .documentDirectory)
AF.download("https://speed.hetzner.de/10GB.bin", to: destination)
.downloadProgress(queue: queue) { progress in
print(progress.fractionCompleted)
DispatchQueue.main.async {
self.downloadProgress = progress.fractionCompleted
}
}
.response { response in
print(response)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
这里的问题是您实际上是在用更新向 UI 线程发送垃圾邮件,因为 alamofire 经常调用提供给 downloadProgress
的闭包(查看控制台打印)。您需要稍微错开 AF 进度的更新,以便按下按钮关闭警报可以注册(在 Combine 中,这称为去抖动)。我在这里所做的是添加一个小的时间计数器,以便它仅每 1
秒更新一次进度。这些更新之间的时间使 UI 线程可以自由响应点击等
import SwiftUI
import Alamofire
struct ContentView: View {
@StateObject var viewModel: ViewModel = ViewModel()
@State private var showAlert = false
var body: some View {
VStack {
Button("Show Alert") {
showAlert.toggle()
}
Button("Start download") {
viewModel.startDownload()
}
if viewModel.showProgressView {
ProgressView("Downloading…", value: viewModel.downloadProgress, total: 1.0)
.progressViewStyle(.linear)
}
}
.alert(isPresented: $showAlert) {
Alert(
title: Text("Text"),
dismissButton: .cancel()
)
}
}
}
class ViewModel: ObservableObject {
@Published var currentDownload: DownloadRequest? = nil
@Published var downloadProgress: Double = 0.0
@Published var showProgressView: Bool = false
func startDownload() {
print("Function called!")
showProgressView.toggle()
let queue = DispatchQueue(label: "net", qos: .userInitiated)
let destination = DownloadRequest.suggestedDownloadDestination(for: .documentDirectory)
var last = Date()
AF.download("https://speed.hetzner.de/10GB.bin", to: destination)
.downloadProgress(queue:queue) { progress in
print(progress.fractionCompleted)
if Date().timeIntervalSince(last) > 1 {
last = Date()
DispatchQueue.main.async {
self.downloadProgress = progress.fractionCompleted
}
}
}
.response { response in
// print(response)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}