@AppStorage 值更改时 SwiftUI 小部件不会更新

问题描述 投票:0回答:5

我试图让 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")
    }
}
swift swiftui widget widgetkit
5个回答
6
投票

您需要确保在调用 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
 也可能会有所帮助。


4
投票
确保将您的应用程序组添加到签名和功能中的两个目标


0
投票

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) } } }
    

0
投票

AppStorage+Widget方法有两个基本问题:

    您不能在 SwiftUI
  1. @AppStorage
     之外使用 
    View
     - 尤其是在 
    IntentTimelineProvider
     中。
  2. 小部件视图是
  3. 静态 - 即使您在小部件视图中使用@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) } }
    

0
投票
更改 appStorage/默认值之一后更新小部件

WidgetCenter.shared.reloadAllTimelines()

© www.soinside.com 2019 - 2024. All rights reserved.