Is it possible to create text label in Widget that will show current time and will be updating in real-time?
Trying to create clock widget, but widget is updating only 1 time each 5 minutes.
Your app can tell WidgetKit to request a new timeline when something affects a widget’s current timeline. In the game widget example above, if the app receives a push notification indicating a teammate has given the character a healing potion, the app can tell WidgetKit to reload the timeline and update the widget’s content.
Even though your widget doesn’t run continually, it can display time-based information that WidgetKit updates live. For example, it might display a countdown timer that continues to count down even if your widget extension isn’t running. For more information, see Displaying Dynamic Dates in Widgets.
For cases such as system appearance changes or system locale changes, don’t request a timeline reload from your app. The system updates your widgets automatically. Although your widget timeline provider drives your reload schedule, WidgetKit sometimes reloads your widget to help keep its content fresh.
WidgetKit gets a timeline from your provider, and uses it to track when to update your widget. A timeline is an array of TimelineEntry objects. Each entry in the timeline has a date and time, and additional information the widget needs to display its view.
A possible solution is to use the time
date style:
/// A style displaying only the time component for a date.
///
/// Text(event.startDate, style: .time)
///
/// Example output:
/// 11:23PM
public static let time: Text.DateStyle
Entry
with a Date
property:struct SimpleEntry: TimelineEntry {
let date: Date
}
Entry
every minute until the next midnight:struct SimpleProvider: TimelineProvider {
...
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> Void) {
var entries = [SimpleEntry]()
let currentDate = Date()
let midnight = Calendar.current.startOfDay(for: currentDate)
let nextMidnight = Calendar.current.date(byAdding: .day, value: 1, to: midnight)!
for offset in 0 ..< 60 * 24 {
let entryDate = Calendar.current.date(byAdding: .minute, value: offset, to: midnight)!
entries.append(SimpleEntry(date: entryDate))
}
let timeline = Timeline(entries: entries, policy: .after(nextMidnight))
completion(timeline)
}
}
time
style:struct SimpleWidgetEntryView: View {
var entry: SimpleProvider.Entry
var body: some View {
Text(entry.date, style: .time)
}
}
If you want to customise the date format you can use your own DateFormatter
:
struct SimpleWidgetEntryView: View {
var entry: SimpleProvider.Entry
static let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "HH:mm"
return formatter
}()
var body: some View {
Text("\(entry.date, formatter: Self.dateFormatter)")
}
}
Here is a GitHub repository with different Widget examples including the Clock Widget.
Thanks very much for @pawello222's answer, but the problem with this solution is that too many stuff (24 * 60) are saved in the Timeline.
Once the widget's view contains a lot of elements (like 5 or more Text(...) in my case), then the widget will completely stop rendering on the ios real device, and xcode will report this:
2020-12-03 11:22:46.094442+0800 PixelClockWidgetExtension[660:53387]
[default] -[EXSwiftUI_Subsystem beginUsing:withBundle:] unexpectedly called multiple times.
I think one possible solution is to divide the daily time into several small pieces and save them in the Timeline multiple times.
My Environment:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With