SwiftUI如何使用同一时钟视图展示不同时区的时间
实现方案
步骤1:调整Country数据结构,存储时区标识而非固定时间
原来的Country存储的是初始化时一次性生成的固定时间字符串,不会实时更新,替换为存储时区标识符即可:
struct Country: Identifiable { let id = UUID() let name: String let timeZoneIdentifier: String } let countryList: [Country] = [ Country(name: "纽约", timeZoneIdentifier: "America/New_York"), Country(name: "巴黎", timeZoneIdentifier: "Europe/Paris") ]
步骤2:扩展Date支持自定义时区的时间/角度计算
原来的Date计算属性默认使用系统当前时区,新增支持传入时区的方法:
extension Date { func hour(in timeZone: TimeZone) -> Double { var calendar = Calendar.current calendar.timeZone = timeZone return Double(calendar.component(.hour, from: self)) } func minutes(in timeZone: TimeZone) -> Double { var calendar = Calendar.current calendar.timeZone = timeZone return Double(calendar.component(.minute, from: self)) } func seconds(in timeZone: TimeZone) -> Double { var calendar = Calendar.current calendar.timeZone = timeZone return Double(calendar.component(.second, from: self)) } func hourAngle(in timeZone: TimeZone) -> Angle { return Angle(degrees: (360 / 12) * (hour(in: timeZone) + minutes(in: timeZone) / 60)) } func minuteAngle(in timeZone: TimeZone) -> Angle { return Angle(degrees: (minutes(in: timeZone) * 360 / 60)) } func secondAngle(in timeZone: TimeZone) -> Angle { return Angle(degrees: (seconds(in: timeZone) * 360 / 60)) } }
你可以删除原
Date扩展中不带时区参数的hour/minutes/seconds/角度相关计算属性,避免后续调用混淆。
步骤3:为TickHands增加时区参数,调整角度计算逻辑
给指针组件新增时区入参,替换角度计算逻辑为传入时区的版本:
struct TickHands: View { var scale: Double var timeZone: TimeZone // 新增时区参数 @State private var dateTime = Date() private let timer = Timer.publish(every: 1.0, on: .main, in: .common).autoconnect() var body: some View { ZStack { Hand(inset: 65 * scale, angle: dateTime.hourAngle(in: timeZone)) .stroke(style: StrokeStyle(lineWidth: 3 * scale, lineCap: .round, lineJoin: .round)) Hand(inset: 40 * scale, angle: dateTime.minuteAngle(in: timeZone)) .stroke(style: StrokeStyle(lineWidth: 2.5 * scale, lineCap: .round, lineJoin: .round)) Hand(inset: 20 * scale, angle: dateTime.secondAngle(in: timeZone)) .stroke(Color.orange, style: StrokeStyle(lineWidth: 1.5 * scale, lineCap: .round, lineJoin: .round)) Circle().fill(Color.orange).frame(width: 10 * scale) } .onReceive(timer) { input in self.dateTime = input } } }
步骤4:为TestView增加时区参数,向下传递
给时钟外层容器新增时区入参,传递给内部的TickHands:
struct TestView: View { var clockSize: CGFloat = 100 var timeZone: TimeZone // 新增时区参数 var body: some View { GeometryReader { geometry in ZStack { let scale = geometry.size.height / 200 TickHands(scale: scale, timeZone: timeZone) ForEach(0..<60*4) { tick in Ticks.tick(at: tick, scale: scale) } } }.frame(width: clockSize, height: clockSize) } }
步骤5:调整ContentView实现,传递时区并实时刷新时间
新增定时器触发页面刷新,实时更新时间字符串并给TestView传递对应时区:
// 新增时间格式化工具方法 func getLocalTime(in timeZone: TimeZone) -> String { let f = DateFormatter() f.timeZone = timeZone f.dateFormat = "HH:mm" return f.string(from: Date()) } struct ContentView: View { @State private var currentDate = Date() private let timer = Timer.publish(every: 1.0, on: .main, in: .common).autoconnect() var body: some View { NavigationView { List(countryList) { countryItem in // 异常处理:无效时区标识返回空视图,可自行补充降级逻辑 guard let timeZone = TimeZone(identifier: countryItem.timeZoneIdentifier) else { EmptyView() } HStack { Text(countryItem.name + ": " + getLocalTime(in: timeZone)) .frame(height: 80.0) Spacer() TestView(timeZone: timeZone) }.padding(5) } .navigationBarTitle("世界时钟") } .onReceive(timer) { input in currentDate = input } } }
内容的提问来源于stack exchange,提问作者dwayne1929
相关产品推荐
相关产品推荐

