【问题标题】:Updating time text label each minute in WidgetKit在 WidgetKit 中每分钟更新时间文本标签
【发布时间】:2021-01-11 04:14:06
【问题描述】:

是否可以在 Widget 中创建显示当前时间并实时更新的文本标签?

尝试创建时钟小部件,但小部件每 5 分钟仅更新 1 次。

  • 没有帮助创建时间线
  • “使小部件保持最新”不适用于当前时间,仅适用于计时器等。

【问题讨论】:

  • 你能显示你用于时间线的代码吗?更新策略为 5 分钟的时间表应该适用于您的情况
  • @ItayBrenner 更新策略为 5 分钟的时间线将每 5 分钟更新一次时间文本标签,还是更频繁地更新标签?
  • 每5分钟更新一次,我看错了你的问题。您不能像时钟那样更新它,但您可以使用 Timeline .after(1 minute) 策略每秒/分钟更新一次。您只需获取每次执行的实际日期,而不是为此使用计时器。
  • @ItayBrenner 我已经尝试过这个并创建了 1 分钟的时间线,但是这个时间线的执行频率不会超过 5 分钟。苹果对这种处决做了限制
  • Text(date) 有自动更新(请参阅显示动态日期部分),但我认为您无法更新其他任何内容

标签: swift swiftui widget ios14 widgetkit


【解决方案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

  1. 您需要一个简单的 EntryDate 属性:
struct SimpleEntry: TimelineEntry {
    let date: Date
}
  1. 每分钟创建一个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)
    }
}
  1. 使用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,其中包含不同的小部件示例,包括时钟小部件。

【讨论】:

  • 像魅力一样工作。你救了我的命。谢谢!
  • 无论如何要隐藏 Am/Pm 。有人做到了吗?
  • @AmitVerma 我认为您可以在自己的函数中使用 entry.date 来根据需要返回字符串。
  • @Happygallo 您也可以使用DateFormatter - 查看更新后的答案。
  • @Alan 这是一个可能的解决方案:stackoverflow.com/a/64054466/8697793
【解决方案2】:

非常感谢@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.

我认为一种可能的解决方案是将每天的时间分成几个小块,并多次保存在时间轴中。

我的环境:

  • iOS 14.2
  • Xcode 12.2
  • iPhone 12 专业版

【讨论】:

  • @pawello2222 错误报告
  • 也可以看看这个。我的第一个小部件有图像和 1 个文本()。生成 10 个时间线条目。最多落后 1 分钟。转到带有图像的小部件 + 更多 Text()。生成大量时间线条目但只是被忽略,似乎只在获取每个批次时更新(仍在调试)。也许我将不得不将视图渲染为图像才能解决?你的运气好吗?
  • @t9mike 一个事实是:你不能太快地刷新Widget,否则系统会拒绝刷新并在它认为合适的时候重新启动机制。根据我的测试,生成时间线批次最好不要超过 15 分钟。
  • WidgetKit 分配 40 到 70 次总刷新的动态预算。不要这样做,而是显示不准确的内容。 developer.apple.com/documentation/widgetkit/…
【解决方案3】:

我的解决方案是在 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}

和身体一样

var body: some View {
    
    Text(entry.date, style: .time) }

所以现在你有了每分钟更新一次的小部件,没有延迟。

【讨论】:

  • 这只允许最多一小时,之后,小部件上的时间将停止
  • @Ben 刚刚再次测试了它。完美运行超过一小时
  • 不要这样做。 WidgetKit 在 24 小时内为您的小部件分配 40 到 70 次总刷新的预算。
  • @strangetimes idk 为什么小部件可以运行超过 3 天(只是不需要更多测试)。
  • 查看文档。这是在测试期间和 iOS 学习期间预期的。在现实世界中,您最终将永远不会获得超过 40 到 70 次的实际刷新。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-06
  • 1970-01-01
  • 2018-11-26
  • 1970-01-01
相关资源
最近更新 更多