我试图让 SwiftUI 小部件在 @AppStorage 的值发生变化时根据命令进行更新。当我加载模拟器时,小部件会从 @AppStorage 更新为正确的值,但无论我尝试什么,都不会再次更新。要在小部件中显示新值,需要关闭并重新打开模拟器。
在应用程序中查看:
import SwiftUI
import WidgetKit
struct MessageView: View {
@AppStorage("message", store: UserDefaults(suiteName: "group.com.suiteName"))
var widgetMessage: String = ""
var body: some View {
VStack {
Button(action: {
self.widgetMessage = "new message"
WidgetCenter.shared.reloadAllTimelines()
}, label: { Text("button") })
}
}
}
小部件文件:
import WidgetKit
import SwiftUI
import Intents
struct Provider: IntentTimelineProvider {
@AppStorage("message", store: UserDefaults(suiteName: "group.com.suiteName"))
var widgetMessage: String = ""
func placeholder(in context: Context) -> SimpleEntry {
SimpleEntry(date: Date(), message: "Have a great day!", configuration: ConfigurationIntent())
}
func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {
let entry = SimpleEntry(date: Date(), message: "Have a great day!", configuration: configuration)
completion(entry)
}
func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
let entry = SimpleEntry(date: Date(), message: widgetMessage, configuration: configuration)
let timeline = Timeline(entries: [entry], policy: .never)
completion(timeline)
}
}
struct SimpleEntry: TimelineEntry {
let date: Date
let message: String
let configuration: ConfigurationIntent
}
struct statusWidgetEntryView : View {
var entry: Provider.Entry
var body: some View {
VStack{
Text(entry.message)
}
}
}
@main
struct statusWidget: Widget {
let kind: String = "StatusWidget"
var body: some WidgetConfiguration {
IntentConfiguration(
kind: kind,
intent: ConfigurationIntent.self,
provider: Provider()
) { entry in
statusWidgetEntryView(entry: entry)
}
.configurationDisplayName("Note Widget")
.description("Display note from a friend or group")
}
}
您需要确保在调用 WidgetCenter.shared.reloadAllTimelines()
之前
将新值写入磁盘,我认为
@AppStorage
没有办法做到这一点。在您的应用程序中,尝试直接设置 UserDefaults,然后在重新加载小部件之前调用
synchronize()
:
Button(action: {
let userDefaults = UserDefaults(suiteName: "group.com.suiteName")!
userDefaults.set("new message", forKey: "message")
userDefaults.synchronize()
WidgetCenter.shared.reloadAllTimelines()
}, label: { Text("button") })
我知道文档声称不再需要synchronize()
,但这是唯一对我有用的东西。 ́\_(ツ)_/́在您的小部件中使用
UserDefaults
而不是
@AppStorage
也可能会有所帮助。
AppStorage
应该在视野中,其他所有内容都保留在条目中
struct statusWidgetEntryView : View {
@AppStorage("message", store: UserDefaults(suiteName: "group.com.suiteName"))
var widgetMessage: String = ""
var entry: Provider.Entry
var body: some View {
VStack{
Text(widgetMessage)
}
}
}
AppStorage+Widget方法有两个基本问题:
@AppStorage
之外使用
View
- 尤其是在
IntentTimelineProvider
中。
@AppStorage
,该值也只会被读取一次(击败了
@AppStorage
的要点)。
手动从UserDefaults
读取值:
struct Provider: IntentTimelineProvider {
let userDefaults = UserDefaults(suiteName: "group.com.suiteName")!
func placeholder(in context: Context) -> SimpleEntry {
SimpleEntry(date: Date(), message: "Have a great day!", configuration: ConfigurationIntent())
}
func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {
let entry = SimpleEntry(date: Date(), message: "Have a great day!", configuration: configuration)
completion(entry)
}
func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
// read on every timeline refresh
let widgetMessage = userDefaults.string(forKey: "message") ?? ""
let entry = SimpleEntry(date: Date(), message: widgetMessage, configuration: configuration)
let timeline = Timeline(entries: [entry], policy: .never)
completion(timeline)
}
}
WidgetCenter.shared.reloadAllTimelines()