SwiftUI - 创建弹出窗口,在执行长代码时显示更新进度消息
SwiftUI - Create popup that displays an updating progress message when executing long code
我正在创建一个应用程序,其中包含一些需要执行一些代码的代码。我希望它挂起应用程序,因为我不希望用户在执行时进行任何更改。这是一个多平台应用程序,因此在 macOS 中执行时,它会自动将鼠标更改为滚动球图像,以便正常工作。在 iOS 中根本没有反馈。我想创建一个弹出窗口(思考警报但不太挑剔),显示一条更新消息,向用户显示正在发生的事情,并明确表示他们必须等待。
现在我的视图调用一个 class 来执行代码,所以我想以某种方式传递一个在 class 中更新但在视图中实时可见的变量。理想情况下,我希望能够使用它并每次从其他视图调用不同的方法,但仍然使用带有消息的弹出窗口在代码执行时更新用户。
为了简化这一点,我创建了一个迷你项目,但我无法让它在 macOS 或 iOS 上运行,因为视图(应用程序)直到代码执行完毕后才会更新(也有打印语句以了解发生了什么)。我一直在尝试 @StateObject、@Published、ObservableObject 等无济于事。
代码:ContentView.swift
import SwiftUI
import CoreData
struct ContentView: View {
@StateObject var progress = myProgress()
@State var showProgress:Bool = false
var body: some View {
NavigationView {
VStack {
Button(action: {
print("Pressed Button 1")
showProgress = true
}, label: {
Text("Execute Option")
})
.sheet(isPresented: $showProgress, content: {
Text(progress.message)
.onAppear() {
Task {
let longMethod = longMethodsCalled(currProgress: progress)
print("About to call method - \(progress.message)")
let error = longMethod.exampleOne(title: "My Passed In Title")
// Here I can use the returned value if it's an object or now if it passed if it's String?
print("Error: \(error ?? "No error")")
print("after printing error - \(progress.message)")
}
// If this is commented out it just shows the last message after code finishes
showProgress = false
}
})
}
}
}
}
其他文件:longMethodsCalled.swift
import Foundation
import SwiftUI
class myProgress: ObservableObject {
@Published var message = "progressing..."
func changeMessage(newMessage:String) {
message = newMessage
print("Message changing. Now: \(message)")
self.objectWillChange.send()
}
}
class longMethodsCalled {
@State var progress: myProgress
init(currProgress:myProgress) {
progress = currProgress
}
public func exampleOne(title:String) -> String? {
print("in example one - \(title)")
progress.changeMessage(newMessage: "Starting example one")
sleep(1)
print("after first sleep")
progress.changeMessage(newMessage: "Middle of example one")
sleep(1)
progress.changeMessage(newMessage: "About to return - example one")
return "result of example one"
}
}
我想我想知道这是否可能?如果是这样,我该怎么做。我不知道我是快吃午饭了还是完全没吃午饭了。我会真的喜欢一种在我的代码执行时更新我的用户的方法。
感谢所有帮助。
这是一个使用绑定在另一个结构中执行所有视图更新的示例。它正在使用异步和等待。对于sleep()
,它使用不锁定队列的Task.sleep
。
struct LongMethodCallMessage: View {
@State var showProgress:Bool = false
@State var progressViewMessage: String = "will do something"
var body: some View {
NavigationView {
VStack {
Button(action: {
print("Pressed Button 1")
progressViewMessage = "Pressed Button 1"
showProgress = true
}, label: {
// text will be return value
// so one can see that it ran
Text(progressViewMessage)
})
.sheet(isPresented: $showProgress, content: {
// create the vue that will display the progress
TheLongTaskView(progressViewMessage: $progressViewMessage, showProgress: $showProgress)
})
}
}
}
}
struct TheLongTaskView: View, LongMethodsCalledMessage {
@Binding var progressViewMessage: String
@Binding var showProgress: Bool
var body: some View {
Text(progressViewMessage)
.onAppear() {
// create the task setting this as delegate
// to receive message update
Task {
let longMethod = LongMethodsCalled(delegate: self)
print("About to call method - \(progressViewMessage)")
let error = await longMethod.exampleOne(title: "My Passed In Title")
// Here I can use the returned value if it's an object or now if it passed if it's String?
print("Error: \(error ?? "No error")")
print("after printing error - \(progressViewMessage)")
// save the error message and close view
progressViewMessage = error!
showProgress = false
}
}
}
// updating the text
func changeMessage(newMessage:String) {
print("changeMessage: \(newMessage)")
progressViewMessage = newMessage
}
}
// a protocol to update the text in the long running task
protocol LongMethodsCalledMessage {
func changeMessage(newMessage:String)
}
class LongMethodsCalled {
var delegate: LongMethodsCalledMessage
init(delegate: LongMethodsCalledMessage) {
self.delegate = delegate
}
// make the method async
public func exampleOne(title:String) async -> String? {
print("in example one - \(title)")
self.delegate.changeMessage(newMessage: "Starting example one")
// this wait enable the text to update
// the sleep() may lock and prevent main queue to run
try! await Task.sleep(nanoseconds: 2_000_000_000)
print("after first sleep")
self.delegate.changeMessage(newMessage: "Middle of example one")
try! await Task.sleep(nanoseconds: 2_000_000_000)
print("after second sleep")
self.delegate.changeMessage(newMessage: "About to return - example one")
return "result of example one"
}
}
我正在创建一个应用程序,其中包含一些需要执行一些代码的代码。我希望它挂起应用程序,因为我不希望用户在执行时进行任何更改。这是一个多平台应用程序,因此在 macOS 中执行时,它会自动将鼠标更改为滚动球图像,以便正常工作。在 iOS 中根本没有反馈。我想创建一个弹出窗口(思考警报但不太挑剔),显示一条更新消息,向用户显示正在发生的事情,并明确表示他们必须等待。
现在我的视图调用一个 class 来执行代码,所以我想以某种方式传递一个在 class 中更新但在视图中实时可见的变量。理想情况下,我希望能够使用它并每次从其他视图调用不同的方法,但仍然使用带有消息的弹出窗口在代码执行时更新用户。
为了简化这一点,我创建了一个迷你项目,但我无法让它在 macOS 或 iOS 上运行,因为视图(应用程序)直到代码执行完毕后才会更新(也有打印语句以了解发生了什么)。我一直在尝试 @StateObject、@Published、ObservableObject 等无济于事。
代码:ContentView.swift
import SwiftUI
import CoreData
struct ContentView: View {
@StateObject var progress = myProgress()
@State var showProgress:Bool = false
var body: some View {
NavigationView {
VStack {
Button(action: {
print("Pressed Button 1")
showProgress = true
}, label: {
Text("Execute Option")
})
.sheet(isPresented: $showProgress, content: {
Text(progress.message)
.onAppear() {
Task {
let longMethod = longMethodsCalled(currProgress: progress)
print("About to call method - \(progress.message)")
let error = longMethod.exampleOne(title: "My Passed In Title")
// Here I can use the returned value if it's an object or now if it passed if it's String?
print("Error: \(error ?? "No error")")
print("after printing error - \(progress.message)")
}
// If this is commented out it just shows the last message after code finishes
showProgress = false
}
})
}
}
}
}
其他文件:longMethodsCalled.swift
import Foundation
import SwiftUI
class myProgress: ObservableObject {
@Published var message = "progressing..."
func changeMessage(newMessage:String) {
message = newMessage
print("Message changing. Now: \(message)")
self.objectWillChange.send()
}
}
class longMethodsCalled {
@State var progress: myProgress
init(currProgress:myProgress) {
progress = currProgress
}
public func exampleOne(title:String) -> String? {
print("in example one - \(title)")
progress.changeMessage(newMessage: "Starting example one")
sleep(1)
print("after first sleep")
progress.changeMessage(newMessage: "Middle of example one")
sleep(1)
progress.changeMessage(newMessage: "About to return - example one")
return "result of example one"
}
}
我想我想知道这是否可能?如果是这样,我该怎么做。我不知道我是快吃午饭了还是完全没吃午饭了。我会真的喜欢一种在我的代码执行时更新我的用户的方法。
感谢所有帮助。
这是一个使用绑定在另一个结构中执行所有视图更新的示例。它正在使用异步和等待。对于sleep()
,它使用不锁定队列的Task.sleep
。
struct LongMethodCallMessage: View {
@State var showProgress:Bool = false
@State var progressViewMessage: String = "will do something"
var body: some View {
NavigationView {
VStack {
Button(action: {
print("Pressed Button 1")
progressViewMessage = "Pressed Button 1"
showProgress = true
}, label: {
// text will be return value
// so one can see that it ran
Text(progressViewMessage)
})
.sheet(isPresented: $showProgress, content: {
// create the vue that will display the progress
TheLongTaskView(progressViewMessage: $progressViewMessage, showProgress: $showProgress)
})
}
}
}
}
struct TheLongTaskView: View, LongMethodsCalledMessage {
@Binding var progressViewMessage: String
@Binding var showProgress: Bool
var body: some View {
Text(progressViewMessage)
.onAppear() {
// create the task setting this as delegate
// to receive message update
Task {
let longMethod = LongMethodsCalled(delegate: self)
print("About to call method - \(progressViewMessage)")
let error = await longMethod.exampleOne(title: "My Passed In Title")
// Here I can use the returned value if it's an object or now if it passed if it's String?
print("Error: \(error ?? "No error")")
print("after printing error - \(progressViewMessage)")
// save the error message and close view
progressViewMessage = error!
showProgress = false
}
}
}
// updating the text
func changeMessage(newMessage:String) {
print("changeMessage: \(newMessage)")
progressViewMessage = newMessage
}
}
// a protocol to update the text in the long running task
protocol LongMethodsCalledMessage {
func changeMessage(newMessage:String)
}
class LongMethodsCalled {
var delegate: LongMethodsCalledMessage
init(delegate: LongMethodsCalledMessage) {
self.delegate = delegate
}
// make the method async
public func exampleOne(title:String) async -> String? {
print("in example one - \(title)")
self.delegate.changeMessage(newMessage: "Starting example one")
// this wait enable the text to update
// the sleep() may lock and prevent main queue to run
try! await Task.sleep(nanoseconds: 2_000_000_000)
print("after first sleep")
self.delegate.changeMessage(newMessage: "Middle of example one")
try! await Task.sleep(nanoseconds: 2_000_000_000)
print("after second sleep")
self.delegate.changeMessage(newMessage: "About to return - example one")
return "result of example one"
}
}