Swift/Widget扩展 – 每秒更新时间

huangapple go评论65阅读模式
英文:

Swift/Widget Extension - Time update every second

问题

Here's the translated code you requested:

这是我的第一篇帖子,请告诉我是否需要更多信息。

我正在创建一个小部件,它将同时显示两个位置的当前时间。

这是我目前的代码:

```swift
import SwiftUI
import WidgetKit

struct TimeWidgetView: View {
    var localTime: String
    var foreignTime: String
    
    var body: some View {
        HStack(spacing: 16) {
            VStack{
                Text("本地时间")
                    .font(.title2)
                    .fontWeight(.bold)
                    .foregroundColor(.primary)
                
                Text(localTime)
                    .font(.title3)
                    .fontWeight(.semibold)
                    .foregroundColor(.primary)
                    .multilineTextAlignment(.center)
                    .lineLimit(1)
                    .minimumScaleFactor(0.7)
            }
            
            VStack{
                Text("中国时间")
                    .font(.title2)
                    .fontWeight(.bold)
                    .foregroundColor(.primary)
                
                Text(foreignTime)
                    .font(.title3)
                    .fontWeight(.semibold)
                    .foregroundColor(.primary)
                    .multilineTextAlignment(.center)
                    .lineLimit(1)
                    .minimumScaleFactor(0.7)
            }
            
        }
        .padding()
    }
}

struct TimeWidget: Widget {
    private let kind: String = "TimeWidget"

    var body: some WidgetConfiguration {
        StaticConfiguration(kind: kind, provider: TimeProvider()) { entry in
            TimeWidgetView(localTime: entry.localTime, foreignTime: entry.foreignTime)
        }
        .configurationDisplayName("时间小部件")
        .description("显示本地时间和中国时间。")
    }
}

struct TimeProvider: TimelineProvider {
    func placeholder(in context: Context) -> TimeEntry {
        TimeEntry(localTime: "00:00", foreignTime: "00:00")
    }

    func getSnapshot(in context: Context, completion: @escaping (TimeEntry) -> Void) {
        let entry = TimeEntry(localTime: "12:34", foreignTime: "23:34")
        completion(entry)
    }

    func getTimeline(in context: Context, completion: @escaping (Timeline<TimeEntry>) -> Void) {
        let currentDate = Date()
        let calendar = Calendar.current
        
        let localTimeFormatter = DateFormatter()
        localTimeFormatter.dateFormat = "HH:mm:ss"
        let localTime = localTimeFormatter.string(from: currentDate)
        
        let foreignTimeFormatter = DateFormatter()
        foreignTimeFormatter.timeZone = TimeZone(identifier: "Asia/Shanghai") // 设置时区为中国
        foreignTimeFormatter.dateFormat = "HH:mm:ss"
        let foreignTime = foreignTimeFormatter.string(from: currentDate)
        
        let entry = TimeEntry(localTime: localTime, foreignTime: foreignTime)
        let timeline = Timeline(entries: [entry], policy: .atEnd)
        completion(timeline)
    }
}

struct TimeEntry: TimelineEntry {
    var date: Date = Date()
    var localTime: String
    var foreignTime: String
}

struct TimeWidget_Previews: PreviewProvider {
    static var previews: some View {
        TimeWidgetView(localTime: "12:34:56", foreignTime: "23:34:45")
            .previewContext(WidgetPreviewContext(family: .systemMedium))
    }
}

关于您的问题,您提到希望使小部件每秒更新以充当数字时钟。您可以尝试以下方法,但要注意这可能会影响电池寿命:

func getTimeline(in context: Context, completion: @escaping (Timeline<TimeEntry>) -> Void) {
    var entries: [TimeEntry] = []
    
    // 使用定时器每半秒更新小部件作为时钟
    let timer = Timer.publish(every: 0.5, on: .main, in: .common).autoconnect()
    let formatter = DateFormatter()
    formatter.dateFormat = "HH:mm:ss"
    for second in 0...59 {
        let currentDate = Date().addingTimeInterval(TimeInterval(second))
        
        let localTime = formatter.string(from: currentDate)
        
        let foreignTimeFormatter = DateFormatter()
        foreignTimeFormatter.timeZone = TimeZone(identifier: "Asia/Shanghai") // 设置时区为中国
        foreignTimeFormatter.dateFormat = "HH:mm:ss"
        let foreignTime = foreignTimeFormatter.string(from: currentDate)
        
        let entry = TimeEntry(date: currentDate, localTime: localTime, foreignTime: foreignTime)
        entries.append(entry)
    }
    
    let timeline = Timeline(entries: entries, policy: .atEnd)
    completion(timeline)
}

这段代码使用定时器每半秒生成一个新的时间条目,从而使小部件看起来像一个数字时钟。但请注意,这可能会对设备的电池寿命产生影响,因为它会持续消耗处理能力。

英文:

This is my first post, so let me know if any more info is needed.

I am creating a widget that will display the current time for two locations at the same time.

This is the current code I have:

import SwiftUI
import WidgetKit
struct TimeWidgetView: View {
var localTime: String
var foreignTime: String
var body: some View {
HStack(spacing: 16) {
VStack{
Text(&quot;Local Time&quot;)
.font(.title2)
.fontWeight(.bold)
.foregroundColor(.primary)
Text(localTime)
.font(.title3)
.fontWeight(.semibold)
.foregroundColor(.primary)
.multilineTextAlignment(.center)
.lineLimit(1)
.minimumScaleFactor(0.7)
}
VStack{
Text(&quot;China Time&quot;)
.font(.title2)
.fontWeight(.bold)
.foregroundColor(.primary)
Text(foreignTime)
.font(.title3)
.fontWeight(.semibold)
.foregroundColor(.primary)
.multilineTextAlignment(.center)
.lineLimit(1)
.minimumScaleFactor(0.7)
}
}
.padding()
}
}
struct TimeWidget: Widget {
private let kind: String = &quot;TimeWidget&quot;
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: TimeProvider()) { entry in
TimeWidgetView(localTime: entry.localTime, foreignTime: entry.foreignTime)
}
.configurationDisplayName(&quot;Time Widget&quot;)
.description(&quot;Displays local time and China time.&quot;)
}
}
struct TimeProvider: TimelineProvider {
func placeholder(in context: Context) -&gt; TimeEntry {
TimeEntry(localTime: &quot;00:00&quot;, foreignTime: &quot;00:00&quot;)
}
func getSnapshot(in context: Context, completion: @escaping (TimeEntry) -&gt; Void) {
let entry = TimeEntry(localTime: &quot;12:34&quot;, foreignTime: &quot;23:34&quot;)
completion(entry)
}
func getTimeline(in context: Context, completion: @escaping (Timeline&lt;TimeEntry&gt;) -&gt; Void) {
let currentDate = Date()
let calendar = Calendar.current
let localTimeFormatter = DateFormatter()
localTimeFormatter.dateFormat = &quot;HH:mm:ss&quot;
let localTime = localTimeFormatter.string(from: currentDate)
let foreignTimeFormatter = DateFormatter()
foreignTimeFormatter.timeZone = TimeZone(identifier: &quot;Asia/Shanghai&quot;) // Set time zone to China
foreignTimeFormatter.dateFormat = &quot;HH:mm:ss&quot;
let foreignTime = foreignTimeFormatter.string(from: currentDate)
let entry = TimeEntry(localTime: localTime, foreignTime: foreignTime)
let timeline = Timeline(entries: [entry], policy: .atEnd)
completion(timeline)
}
}
struct TimeEntry: TimelineEntry {
var date: Date = Date()
var localTime: String
var foreignTime: String
}
struct TimeWidget_Previews: PreviewProvider {
static var previews: some View {
TimeWidgetView(localTime: &quot;12:34:56&quot;, foreignTime: &quot;23:34:45&quot;)
.previewContext(WidgetPreviewContext(family: .systemMedium))
}
}

When I run the app/widget from Xcode, it displays the correct time, but the time doesn't change.. I don't understand how can I accomplish this.

How can I make it update every second so it works as a digital clock?

EDIT: I may found a solution, but not sure if this is a good way to go (thinking about battery life):

func getTimeline(in context: Context, completion: @escaping (Timeline&lt;TimeEntry&gt;) -&gt; Void) {
var entries: [TimeEntry] = []
// Use a Timer to update the widget every half second as a clock
let timer = Timer.publish(every: 0.5, on: .main, in: .common).autoconnect()
let formatter = DateFormatter()
formatter.dateFormat = &quot;HH:mm:ss&quot;
for second in 0...59 {
let currentDate = Date().addingTimeInterval(TimeInterval(second))
let localTime = formatter.string(from: currentDate)
let foreignTimeFormatter = DateFormatter()
foreignTimeFormatter.timeZone = TimeZone(identifier: &quot;Asia/Shanghai&quot;) // Set time zone to China
foreignTimeFormatter.dateFormat = &quot;HH:mm:ss&quot;
let foreignTime = foreignTimeFormatter.string(from: currentDate)
let entry = TimeEntry(date: currentDate, localTime: localTime, foreignTime: foreignTime)
entries.append(entry)
}
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}

答案1

得分: 1

请查看苹果官方文档:https://developer.apple.com/documentation/widgetkit/keeping-a-widget-up-to-date

我认为如果尝试绕过这些限制,你可能只会受到“惩罚”,你的小组件会更新得更不频繁。

我还检查了是否可以在你的小组件内实现动画,以便你可以实现一个自动移动的时钟指针,而不必每秒手动更新它,但由于 WidgetKit 不支持动画,这也是不可行的。所以,我认为你想实现的目标是不可能的。

英文:

Please check the official documentation from Apple: https://developer.apple.com/documentation/widgetkit/keeping-a-widget-up-to-date

I think if you try to bypass the limitations, you may only get "punished" and your widget will be updated even less frequently.

I also checked if it's possible to implement animations within your widget, so that you can implement a moving clock hand that basically moves by itself without manually updating it every second, but that also doesn't work since animations are not available within WidgetKit. So yeah, I think what you want to achieve is just not possible.

huangapple
  • 本文由 发表于 2023年4月19日 18:53:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/76053660.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定