我試圖弄清楚當一些異步代碼需要一些時間運行時如何向我的用戶顯示訊息。到目前為止,我已經使用我在網上找到的示例創建了一個彈出橫幅,并在我的視圖上使用異步方法的 ObservedObject 將訊息系結在一起,然后從我的異步方法發布值。
我的示例代碼專案位于此處的公共 GitHub 存盤庫中,我將在底部發布代碼。
現在我在從異步方法設定變數時遇到問題:Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates.在線解決方案似乎通過更新@mainActor執行緒上的值來解決這個問題,但我希望這些方法異步運行并更新用戶正在發生的事情。從這個位置更新我的變數的最佳方法是什么?
代碼
在主應用程式中:
var body: some Scene {
WindowGroup {
ContentView(asyncmethod: myAsyncViewModel())
}
}
內容視圖:
import SwiftUI
struct ContentView: View {
@State private var isLoaderPresented = false
@State private var isTopMessagePresented = false
@ObservedObject var asyncmethod: myAsyncViewModel
var body: some View {
VStack {
Spacer()
Button( action: {
Task {
isTopMessagePresented = true
let response = await asyncmethod.thisMethodTakesTime()
// Want to return a string or object so I know what happens.
print("Response Loader: \(response ?? "no response")")
isTopMessagePresented = false
}
},
label: { Text("Run Top Banner Code") }
)
Spacer()
}
.foregroundColor(.black)
.popup(isPresented: isTopMessagePresented, alignment: .top, direction: .top, content: {
Snackbar(showForm: $isTopMessagePresented, asyncmethod: asyncmethod)
})
}
}
struct Snackbar: View {
@Binding var showForm: Bool
@ObservedObject var asyncmethod: myAsyncViewModel
var body: some View {
VStack {
HStack() {
Image(systemName: asyncmethod.imageName)
.resizable()
.aspectRatio(contentMode: ContentMode.fill)
.frame(width: 40, height: 40)
Spacer()
VStack(alignment: .center, spacing: 4) {
Text(asyncmethod.title)
.foregroundColor(.black)
.font(.headline)
Text(asyncmethod.subTitle)
.font(.body)
.foregroundColor(.black)
.frame(maxWidth: .infinity)
}
}
.frame(minWidth: 200)
}
.padding(15)
.frame(maxWidth: .infinity, idealHeight: 100)
.background(Color.black.opacity(0.1))
}
}
我的異步示例方法:
import Foundation
class myAsyncViewModel: ObservableObject {
@Published var imageName: String = "questionmark"
@Published var title: String = "title"
@Published var subTitle: String = "subtitle"
func thisMethodTakesTime() async -> String? {
print("In method: \(imageName), \(title), \(subTitle)")
title = "MY METHOD"
subTitle = "Starting out!"
print("In method. Starting \(title)")
subTitle = "This is the message"
print("Sleeping")
try? await Task.sleep(nanoseconds: 1_000_000_000)
subTitle = "Between"
try? await Task.sleep(nanoseconds: 1_000_000_000)
print("After sleep. Ending")
subTitle = "About to return. Success!"
print("In method: \(imageName), \(title), \(subTitle)")
return "RETURN RESULT"
}
}
以及彈出視窗的支持檔案:
import SwiftUI
struct Popup<T: View>: ViewModifier {
let popup: T
let isPresented: Bool
let alignment: Alignment
let direction: Direction
// 1.
init(isPresented: Bool, alignment: Alignment, direction: Direction, @ViewBuilder content: () -> T) {
self.isPresented = isPresented
self.alignment = alignment
self.direction = direction
popup = content()
}
// 2.
func body(content: Content) -> some View {
content
.overlay(popupContent())
}
// 3.
@ViewBuilder private func popupContent() -> some View {
GeometryReader { geometry in
if isPresented {
withAnimation {
popup
.transition(.offset(x: 0, y: direction.offset(popupFrame: geometry.frame(in: .global))))
.frame(width: geometry.size.width, height: geometry.size.height, alignment: alignment)
}
}
}
}
}
extension Popup {
enum Direction {
case top, bottom
func offset(popupFrame: CGRect) -> CGFloat {
switch self {
case .top:
let aboveScreenEdge = -popupFrame.maxY
return aboveScreenEdge
case .bottom:
let belowScreenEdge = UIScreen.main.bounds.height - popupFrame.minY
return belowScreenEdge
}
}
}
}
private extension GeometryProxy {
var belowScreenEdge: CGFloat {
UIScreen.main.bounds.height - frame(in: .global).minY
}
}
extension View {
func popup<T: View>(
isPresented: Bool,
alignment: Alignment = .center,
direction: Popup<T>.Direction = .bottom,
@ViewBuilder content: () -> T
) -> some View {
return modifier(Popup(isPresented: isPresented, alignment: alignment, direction: direction, content: content))
}
}
同樣,所有這些都可以在我的 GitHub 頁面中找到。
uj5u.com熱心網友回復:
您可以在分配給已發布變數時使用“@MainActor”注釋可觀察類或僅使用函式或使用 DispatchQueue.main.async。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/522582.html
標籤:迅速代码异步
上一篇:如何從SwiftDangerFile訪問BitriseiOS測驗結果?
下一篇:SQL注入
