在 WidgetKit 中每分钟更新时间文本标签
Updating time text label each minute in WidgetKit
是否可以在 Widget 中创建显示当前时间并实时更新的文本标签?
正在尝试创建时钟小部件,但小部件每 5 分钟仅更新 1 次。
- 时间轴的创建没有帮助
- “使小部件保持最新”不适用于当前时间,仅适用于计时器等
一个可能的解决方案是使用 time
日期样式:
/// 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
和 Date
属性:
struct SimpleEntry: TimelineEntry {
let date: Date
}
- 每分钟创建一个
Entry
,直到 下一个午夜:
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
样式显示日期:
struct SimpleWidgetEntryView: View {
var entry: SimpleProvider.Entry
var body: some View {
Text(entry.date, style: .time)
}
}
如果您想自定义日期格式,您可以使用自己的 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)")
}
}
这是一个 GitHub repository 包含时钟小部件在内的不同小部件示例。
非常感谢@pawello222 的回答,但此解决方案的问题是时间轴中保存了太多内容 (24 * 60)。
一旦小部件的视图包含大量元素(在我的例子中是 5 个或更多 Text(...)),那么小部件将完全停止在 ios 真实设备上呈现,并且 xcode 将报告:
2020-12-03 11:22:46.094442+0800 PixelClockWidgetExtension[660:53387]
[default] -[EXSwiftUI_Subsystem beginUsing:withBundle:] unexpectedly called multiple times.
我觉得一个可能的解决办法是把每天的时间分成几个小块,多次保存在Timeline中。
我的环境:
- ios 14.2
- Xcode 12.2
- iPhone 12 亲
我的解决方案是每分钟 0 秒更新一次小部件。
使用此扩展程序以 0 秒的分钟获取当前时间。
extension Date {
var zeroSeconds: Date? {
let calendar = Calendar.current
let dateComponents = calendar.dateComponents([.year, .month, .day, .hour, .minute], from: self)
return calendar.date(from: dateComponents)
}}
并使用这个 getTimeline 函数
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
var entries: [SimpleEntry] = []
// Generate a timeline consisting of five entries an hour apart, starting from the current date.
let date = Date().zeroSeconds!
for hourOffset in 0 ..< 60 {
let entryDate = Calendar.current.date(byAdding: .minute , value: hourOffset, to: date)!
print(entryDate)
let entry = SimpleEntry(date: entryDate)
entries.append(entry)
}
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
simpleEntry 与 Xcode
中的默认示例相同
struct SimpleEntry: TimelineEntry {
let date: Date}
同样body
var body: some View {
Text(entry.date, style: .time) }
所以现在您有了每分钟更新一次的小部件,没有延迟。
是否可以在 Widget 中创建显示当前时间并实时更新的文本标签?
正在尝试创建时钟小部件,但小部件每 5 分钟仅更新 1 次。
- 时间轴的创建没有帮助
- “使小部件保持最新”不适用于当前时间,仅适用于计时器等
一个可能的解决方案是使用 time
日期样式:
/// 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
和Date
属性:
struct SimpleEntry: TimelineEntry {
let date: Date
}
- 每分钟创建一个
Entry
,直到 下一个午夜:
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
样式显示日期:
struct SimpleWidgetEntryView: View {
var entry: SimpleProvider.Entry
var body: some View {
Text(entry.date, style: .time)
}
}
如果您想自定义日期格式,您可以使用自己的 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)")
}
}
这是一个 GitHub repository 包含时钟小部件在内的不同小部件示例。
非常感谢@pawello222 的回答,但此解决方案的问题是时间轴中保存了太多内容 (24 * 60)。
一旦小部件的视图包含大量元素(在我的例子中是 5 个或更多 Text(...)),那么小部件将完全停止在 ios 真实设备上呈现,并且 xcode 将报告:
2020-12-03 11:22:46.094442+0800 PixelClockWidgetExtension[660:53387]
[default] -[EXSwiftUI_Subsystem beginUsing:withBundle:] unexpectedly called multiple times.
我觉得一个可能的解决办法是把每天的时间分成几个小块,多次保存在Timeline中。
我的环境:
- ios 14.2
- Xcode 12.2
- iPhone 12 亲
我的解决方案是每分钟 0 秒更新一次小部件。 使用此扩展程序以 0 秒的分钟获取当前时间。
extension Date {
var zeroSeconds: Date? {
let calendar = Calendar.current
let dateComponents = calendar.dateComponents([.year, .month, .day, .hour, .minute], from: self)
return calendar.date(from: dateComponents)
}}
并使用这个 getTimeline 函数
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
var entries: [SimpleEntry] = []
// Generate a timeline consisting of five entries an hour apart, starting from the current date.
let date = Date().zeroSeconds!
for hourOffset in 0 ..< 60 {
let entryDate = Calendar.current.date(byAdding: .minute , value: hourOffset, to: date)!
print(entryDate)
let entry = SimpleEntry(date: entryDate)
entries.append(entry)
}
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
simpleEntry 与 Xcode
中的默认示例相同struct SimpleEntry: TimelineEntry {
let date: Date}
同样body
var body: some View {
Text(entry.date, style: .time) }
所以现在您有了每分钟更新一次的小部件,没有延迟。