How to Update iOS 14 Widget Background Color from the App

How to update iOS 14 Widget background color from the app?

You are accessing UserDefaults in your Widget rather than in the widget's timeline provider. You are also storing your Color in an unnecessarily complicated way.

Here is a simple example that shows you how to save a UIColor into UserDefaults and access it in your widget. Although you are using Color, Color structs can be created from a UIColor. However ColorPicker allows you to create a binding with a CGColor or Color, and CGColor can be converted easily to UIColor.

ContentView

In my ContentView I have created a simple app that uses a ColorPicker with a binding of CGColor. When the color is selected we pass it as a UIColor to the save function. This uses NSKeyedArchiver to convert the UIColor into Data, which can easily be saved into UserDefaults. I use AppStorage to store the Data created from the UIColor.

We then call WidgetCenter.shared.reloadAllTimelines() to make sure that the WidgetCenter knows that we want to update the widgets.

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

MyWidget

Next in the Provider we use property wrapper AppStorage to access the Data that we saved for the color. We do not access AppStorage inside MyWidgetEntryView or inside MyWidget as it will not work there.

Inside my provider I have created a computed property that gets the UIColor from the color data that we stored in AppStorage. This color is then passed to each entry when it is created. This is key to using AppStorage with your widgets, the values must be passed when the entry is created.

MyWidgetEntryView is very simple it just shows the date that it was created and the background color.

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

Here it is working /p>

Widget with color picker

Can't change the iOS14 widget background color

You need to specify full frame, as on below demo

demo

StaticConfiguration(
kind: "xWidget",
provider: xProvider(),
placeholder: Text("Loading...")) { entry in
xWidgetEntryView(entry: entry)
.frame(maxWidth: .infinity, maxHeight: .infinity) // << here !!
.background(Color.green)
}.supportedFamilies([.systemSmall, .systemMedium, .systemLarge])

How do I make my widget update more frequently or update when an action happens within the app

You can always update your widget from your app with the code block below. Please be aware you need to import WidgetKit to your class for calling this function.

WidgetCenter.shared.reloadAllTimelines()

iOS widget background-image goes black after couple of minutes

Since the problem only occurs with these two specific images it seems like the files are either

  1. Not correctly added to the assets folder
  2. Broken or corrupt

Try to generate a completely new file for both pictures (e.g. take screenshots) and replace them with the current images in the assets folder. That should most likely fix your issue.



Related Topics



Leave a reply



Submit