【问题标题】:How to update iOS 14 Widget background color from the app?如何从应用程序更新 iOS 14 小部件背景颜色?
【发布时间】:2021-02-19 18:28:12
【问题描述】:

对于我的小部件,我希望用户能够选择他们选择的小部件背景颜色,但我似乎无法让它工作。

我正在尝试使用 ColorPicker@AppStorage 来做到这一点

ColorPicker("Background Color", selection: Binding(get: {
                    bgColor
                }, set: { newValue in
                    backgroundColor = self.updateColorInAppStorage(color: newValue)
                    bgColor = newValue
                }))
                .frame(width: 200, height: 50)
                .font(.headline)

backgroundColor 是一个 @AppStorage 变量,用于保存 RGB 值

在我的 Widget 扩展类中,我使用这个变量来设置 @main 结构中的小部件背景颜色:

@main
struct MyWidget: Widget {
    @AppStorage("backgroundColor", store: UserDefaults(suiteName: "group.com.MyWidget")) var backgroundColor = ""
    @State private var bgColor = Color(UIColor.systemBackground)
    let kind: String = "Count"

    var body: some WidgetConfiguration {
        IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in
            SubscriberCountEntryView(entry: entry)
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .background(bgColor)
                .onAppear {
                    if (backgroundColor != "") {
                        let rgbArray = backgroundColor.components(separatedBy: ",")
                        if let red = Double(rgbArray[0]), let green = Double(rgbArray[1]), let blue = Double(rgbArray[2]), let alpha = Double(rgbArray[3]) {
                            bgColor = Color(.sRGB, red: red, green: green, blue: blue, opacity: alpha)
                        }
                    }
                }
        }
        .configurationDisplayName("Count")
        .supportedFamilies([.systemSmall, .systemMedium])
    }
}

这似乎只在应用首次安装在设备上时才有效。之后,无论我选择多少次颜色并更新小部件,背景颜色都不会改变。如果我删除并再次安装该应用程序,颜色会改变。

关于如何正确执行此操作的任何想法?

【问题讨论】:

    标签: ios swift swiftui widgetkit


    【解决方案1】:

    您在小部件中访问 UserDefaults,而不是在小部件的时间线提供程序中。您还以一种不必要的复杂方式存储您的颜色。

    这是一个简单的示例,向您展示如何将 UIColor 保存到 UserDefaults 并在您的小部件中访问它。尽管您使用的是 Color,但可以从 UIColor 创建 Color 结构。但是ColorPicker 允许您使用CGColorColor 创建绑定,并且CGColor 可以轻松转换为UIColor

    内容视图

    在我的 ContentView 中,我创建了一个简单的应用程序,该应用程序使用带有 CGColor 绑定的 ColorPicker。选择颜色后,我们将其作为 UIColor 传递给保存函数。这使用NSKeyedArchiver 将 UIColor 转换为 Data,可以轻松地将其保存到 UserDefaults 中。我使用AppStorage 来存储从 UIColor 创建的数据。

    然后我们调用WidgetCenter.shared.reloadAllTimelines() 以确保 WidgetCenter 知道我们要更新小部件。

    import SwiftUI
    import WidgetKit
    
    struct ContentView: View {
    
        @AppStorage("color", store: UserDefaults(suiteName: "group.com.my.app.identifier"))
        var colorData: Data = Data()
    
        @State private var bgColor: CGColor = UIColor.systemBackground.cgColor
    
        var body: some View {
    
            ColorPicker("Color", selection: Binding(get: {
                bgColor
            }, set: { newValue in
                save(color: UIColor(cgColor: newValue))
                bgColor = newValue
            }))
        }
    
        func save(color: UIColor) {
            do {
                colorData = try NSKeyedArchiver.archivedData(withRootObject: color, requiringSecureCoding: false)
                WidgetCenter.shared.reloadAllTimelines()
            } catch let error {
                print("error color key data not saved \(error.localizedDescription)")
            }
        }
    }
    

    我的小部件

    接下来在 Provider 中,我们使用属性包装器 AppStorage 来访问我们为颜色保存的数据。我们不会在MyWidgetEntryViewMyWidget 内部访问AppStorage,因为它在那里不起作用。

    在我的提供程序中,我创建了一个计算属性,该属性从我们存储在AppStorage 中的颜色数据中获取 UIColor。然后在创建时将此颜色传递给每个条目。这是在小部件中使用AppStorage 的关键,必须在创建条目时传递值。

    MyWidgetEntryView 非常简单,它只显示它的创建日期和背景颜色。

    import WidgetKit
    import SwiftUI
    
    struct Provider: TimelineProvider {
    
        @AppStorage("color", store: UserDefaults(suiteName: "group.com.my.app.identifier"))
        var colorData: Data = Data()
    
        func placeholder(in context: Context) -> SimpleEntry {
            SimpleEntry(color: color)
        }
    
        func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {
            let entry = SimpleEntry(color: color)
            completion(entry)
        }
    
        func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
            let entry = SimpleEntry(color: color)
            let timeline = Timeline(entries: [entry], policy: .atEnd)
            completion(timeline)
        }
    
        var color: UIColor {
    
            var color: UIColor?
    
            do {
                color = try NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: colorData)
            } catch let error {
                print("color error \(error.localizedDescription)")
    
            }
            return color ?? .systemBlue
        }
    }
    
    struct SimpleEntry: TimelineEntry {
        let date: Date = Date()
        let color: UIColor
    }
    
    struct MyWidgetEntryView : View {
        var entry: Provider.Entry
    
        var body: some View {
            ZStack {
                Color(entry.color)
                Text(entry.date, style: .time)
            }
       }
    }
    
    @main
    struct MyWidget: Widget {
        let kind: String = "MyWidget"
    
        var body: some WidgetConfiguration {
            StaticConfiguration(kind: kind, provider: Provider()) { entry in
                MyWidgetEntryView(entry: entry)
            }
            .configurationDisplayName("My Widget")
            .description("This is an example widget.")
        }
    }
    

    它正在工作?

    【讨论】:

    • 谢谢!工作完美。但是,一个建议是,在颜色选择器上拖动会导致保存函数被调用数百次,从而不必要地重新加载小部件。因此,我设置了一个通知中心发布者来检查应用程序何时移动到后台,然后重新加载小部件。
    • 您也可以使用.onReceive,使用 ObservableObject 跟踪所选颜色,并为其添加去抖动以限制调用保存函数的次数。这将需要更多的设置,但它会消除发送通知的需要。您可以使用许多选项来减少拨打电话的次数。
    • @Andrew:这也适用于 mac os 小部件吗?您可以发布存储库吗?
    • @Paul 我还没有在 macOS 上尝试过。你在说什么存储库?
    • 以防万一代码不适合您。请记住将应用组功能添加到应用目标和小部件扩展。
    猜你喜欢
    • 1970-01-01
    • 2013-03-20
    • 1970-01-01
    • 2012-06-08
    • 1970-01-01
    • 2021-02-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-12
    相关资源
    最近更新 更多