如何制作一个计时器来显示在 SwiftUI 中已经过了多少时间?
How to make a timer that shows how much time has passed in SwiftUI?
我正在尝试在我的视图顶部添加一个计时器,以显示该视图打开了多长时间。
到目前为止,这是我所拥有的:
@State var isTimerRunning = false
@State private var startTime = Date()
@State private var timerString = "0:0"
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
Text(self.timerString)
.font(Font.system(.largeTitle, design: .monospaced))
.onReceive(timer) { _ in
if self.isTimerRunning {
timerString = String(format: "%.2f", (Date().timeIntervalSince( self.startTime)))
}
}
.onAppear() {
if !isTimerRunning {
timerString = "0:0"
startTime = Date()
}
isTimerRunning.toggle()
}
然而,当我希望它以“12:43”的形式显示秒和分钟时,它以“1.32434234234234”的形式显示毫秒和秒。
使用dateComponents
获取开始时间和当前时间之间的差异。
extension Date {
func passedTime(from date: Date) -> String {
let difference = Calendar.current.dateComponents([.minute, .second], from: date, to: self)
let strMin = String(format: "%02d", difference.minute ?? 00)
let strSec = String(format: "%02d", difference.second ?? 00)
return "\(strMin):\(strSec)"
}
}
并且在视图中
struct ContentView: View {
@State private var isTimerRunning = false
@State private var startTime = Date()
@State private var timerString = "00:00"
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var body: some View {
Text(self.timerString)
.font(Font.system(.largeTitle, design: .monospaced))
.onReceive(timer) { _ in
if self.isTimerRunning {
timerString = Date().passedTime(from: startTime)
}
}
.onAppear() {
if !isTimerRunning {
timerString = "0:0"
startTime = Date()
}
isTimerRunning.toggle()
}
}
}
您可以为此使用 TimeInterval 的扩展。您可以通过更改 formatter.unitsStyle
(.positional
将显示 00:00:00,而 .abbreviated
将显示 0h 0m 0s)和 formatter.zeroFormattingBehavior
变量来很好地自定义字符串。
的致谢名单。
extension TimeInterval {
func format(using units: NSCalendar.Unit) -> String {
let formatter = DateComponentsFormatter()
formatter.allowedUnits = units
formatter.unitsStyle = .positional
formatter.zeroFormattingBehavior = .pad
return formatter.string(from: self) ?? ""
}
}
struct TimerPlayground: View {
@State var isTimerRunning = false
@State private var startTime = Date()
@State var interval = TimeInterval()
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var body: some View {
Text(interval.format(using: [.hour, .minute, .second]))
.font(Font.system(.largeTitle, design: .monospaced))
.onReceive(timer) { _ in
if self.isTimerRunning {
interval = Date().timeIntervalSince(startTime)
}
}
.onAppear() {
if !isTimerRunning {
startTime = Date()
}
isTimerRunning.toggle()
}
}
}
struct TimerPlayground_Previews: PreviewProvider {
static var previews: some View {
TimerPlayground()
}
}
与@Duncan C 的评论一致,这是一个更新版本,它只创建一次格式化程序(本地)以获得更好的性能。
struct TimerPlayground: View {
@State var isTimerRunning = false
@State private var startTime = Date()
@State var interval = TimeInterval()
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
@State var formatter: DateComponentsFormatter = {
let formatter = DateComponentsFormatter()
formatter.allowedUnits = [.hour, .minute, .second]
formatter.unitsStyle = .abbreviated
formatter.zeroFormattingBehavior = .pad
return formatter
}()
var body: some View {
Text(formatter.string(from: interval) ?? "")
.font(Font.system(.largeTitle, design: .monospaced))
.onReceive(timer) { _ in
if self.isTimerRunning {
interval = Date().timeIntervalSince(startTime)
}
}
.onAppear() {
if !isTimerRunning {
startTime = Date()
}
isTimerRunning.toggle()
}
}
}
我正在尝试在我的视图顶部添加一个计时器,以显示该视图打开了多长时间。
到目前为止,这是我所拥有的:
@State var isTimerRunning = false
@State private var startTime = Date()
@State private var timerString = "0:0"
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
Text(self.timerString)
.font(Font.system(.largeTitle, design: .monospaced))
.onReceive(timer) { _ in
if self.isTimerRunning {
timerString = String(format: "%.2f", (Date().timeIntervalSince( self.startTime)))
}
}
.onAppear() {
if !isTimerRunning {
timerString = "0:0"
startTime = Date()
}
isTimerRunning.toggle()
}
然而,当我希望它以“12:43”的形式显示秒和分钟时,它以“1.32434234234234”的形式显示毫秒和秒。
使用dateComponents
获取开始时间和当前时间之间的差异。
extension Date {
func passedTime(from date: Date) -> String {
let difference = Calendar.current.dateComponents([.minute, .second], from: date, to: self)
let strMin = String(format: "%02d", difference.minute ?? 00)
let strSec = String(format: "%02d", difference.second ?? 00)
return "\(strMin):\(strSec)"
}
}
并且在视图中
struct ContentView: View {
@State private var isTimerRunning = false
@State private var startTime = Date()
@State private var timerString = "00:00"
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var body: some View {
Text(self.timerString)
.font(Font.system(.largeTitle, design: .monospaced))
.onReceive(timer) { _ in
if self.isTimerRunning {
timerString = Date().passedTime(from: startTime)
}
}
.onAppear() {
if !isTimerRunning {
timerString = "0:0"
startTime = Date()
}
isTimerRunning.toggle()
}
}
}
您可以为此使用 TimeInterval 的扩展。您可以通过更改 formatter.unitsStyle
(.positional
将显示 00:00:00,而 .abbreviated
将显示 0h 0m 0s)和 formatter.zeroFormattingBehavior
变量来很好地自定义字符串。
extension TimeInterval {
func format(using units: NSCalendar.Unit) -> String {
let formatter = DateComponentsFormatter()
formatter.allowedUnits = units
formatter.unitsStyle = .positional
formatter.zeroFormattingBehavior = .pad
return formatter.string(from: self) ?? ""
}
}
struct TimerPlayground: View {
@State var isTimerRunning = false
@State private var startTime = Date()
@State var interval = TimeInterval()
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var body: some View {
Text(interval.format(using: [.hour, .minute, .second]))
.font(Font.system(.largeTitle, design: .monospaced))
.onReceive(timer) { _ in
if self.isTimerRunning {
interval = Date().timeIntervalSince(startTime)
}
}
.onAppear() {
if !isTimerRunning {
startTime = Date()
}
isTimerRunning.toggle()
}
}
}
struct TimerPlayground_Previews: PreviewProvider {
static var previews: some View {
TimerPlayground()
}
}
与@Duncan C 的评论一致,这是一个更新版本,它只创建一次格式化程序(本地)以获得更好的性能。
struct TimerPlayground: View {
@State var isTimerRunning = false
@State private var startTime = Date()
@State var interval = TimeInterval()
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
@State var formatter: DateComponentsFormatter = {
let formatter = DateComponentsFormatter()
formatter.allowedUnits = [.hour, .minute, .second]
formatter.unitsStyle = .abbreviated
formatter.zeroFormattingBehavior = .pad
return formatter
}()
var body: some View {
Text(formatter.string(from: interval) ?? "")
.font(Font.system(.largeTitle, design: .monospaced))
.onReceive(timer) { _ in
if self.isTimerRunning {
interval = Date().timeIntervalSince(startTime)
}
}
.onAppear() {
if !isTimerRunning {
startTime = Date()
}
isTimerRunning.toggle()
}
}
}