我有ContentView.swift
struct ContentView: View {
@State var seconds: String = "60"
init(_ appDelegate: AppDelegate) {
launchTimer()
}
func launchTimer() {
let timer = DispatchTimeInterval.seconds(5)
let currentDispatchWorkItem = DispatchWorkItem {
print("in timer", "seconds", seconds) // Always `"60"`
print("in timer", "$seconds.wrappedValue", $seconds.wrappedValue) // Always `"60"`
launchTimer()
}
DispatchQueue.main.asyncAfter(deadline: .now() + timer, execute: currentDispatchWorkItem)
}
var body: some View {
TextField("60", text: $seconds)
}
func getSeconds() -> String {
return $seconds.wrappedValue;
}
}
和AppDelegate.swift
class AppDelegate: NSObject, NSApplicationDelegate {
var contentView = ContentView()
func applicationDidFinishLaunching(_ aNotification: Notification) {
launchTimer()
}
func launchTimer() {
print(contentView.getSeconds()) // Always `"60"`
let timer = DispatchTimeInterval.seconds(Int(contentView.getSeconds()) ?? 0)
DispatchQueue.main.asyncAfter(deadline: .now() + timer) {
self.launchTimer()
}
}
@objc func showPreferenceWindow(_ sender: Any?) {
if(window != nil) {
window.close()
}
window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
backing: .buffered, defer: false)
window.isReleasedWhenClosed = false
window.contentView = NSHostingView(rootView: contentView)
window.center()
window.makeKeyAndOrderFront(nil)
NSApplication.shared.activate(ignoringOtherApps: true)
}
}
contentView.getSeconds()
始终返回 60,即使我在内容 View 中修改 TexField
的值(通过在文本字段中手动输入)也是如此。
如何从应用程序委托(delegate)获取状态变量的包装值/实际值?
最佳答案
@State
/@StateObject
是一件棘手的事情,发生的事情是 SwiftUI 将状态值连接到 UI 层次结构中的某个 View 实例。
在您的情况下,@State var秒:String =“60”
连接到下面的 View (简化方案):
NSWindow
-> NSHostingView
-> ContentView <----- this is where the @State usage is valid
正如其他人所说,ContentView
是一个结构体,它是一个值类型,这意味着它的内容会在所有使用站点中复制,因此您不必像类那样拥有唯一的实例,最终会出现多个实例。
只有其中一个实例(SwiftUI 将 View 添加到 UI 树时生成的副本)连接到文本字段并进行更新。
为了让事情变得更有趣,State property wrapper也是一个结构体,这意味着它“遭受”与 View 相同的症状。
这是使用 SwiftUI 的好处之一,与 UIKit 相比,您根本不关心 View 实例。
现在,如果您想使用 AppDelegate 或任何其他地方的秒数值,则必须循环数据存储而不是 View 。
对于初学者来说,更改 View 以接收@Binding
struct ContentView: View {
@Binding var seconds: String = "60"
// the rest of the view code remains the same
然后创建一个存储数据的ObservableObject
,并在创建 View 时注入(inject)其$seconds
绑定(bind):
class AppData: ObservableObject {
@Published var seconds: String = "60"
}
class AppDelegate {
let appData = AppData()
// ... other code left out for clarity
@objc func showPreferenceWindow(_ sender: Any?) {
// ...
let contentView = ContentView(seconds: appData.$seconds
window.contentView = NSHostingView(rootView: contentView)
// ...
}
}
SwiftUI 更多的是关于数据而不是 View 本身,因此如果您想在多个位置访问相同的数据,请确保循环存储而不是数据附加到的 View 。并且还要确保该数据的真实来源是一个类,因为对象引用保证指向相同的内存位置(当然假设引用不会改变)。
关于Swift UI - 如何从外部访问 @State 变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69272227/