I am trying to present a basic time that shows the live time with seconds. When the view appears I call a function that recursively toggles a clockID every 1 second. And the clockId is applied to the Text element such as:
Text(formatTime(date: Date())).font(.caption).id(clockID)
This allows the formatTime
function to be called when the clockID
changes every second and thus shows the new time. But in doing so it seems that the entire view is redrawn when clockID
is toggled. The causes the view to not be very smooth, and when a menu is opened it has a flicking effect. Whats a more efficient way to display the time?
func formatTime(date: Date) -> String {
let formatter = DateFormatter()
formatter.dateFormat = "h:mm:ss"
return formatter.string(from: date)
}
func clockTogg() {
clockID = UUID()
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
if appeared {
clockTogg()
}
}
}
You could try this approach, of isolating the changing part of a View
into a separate View
. This avoids the whole parent view from updating
when a variable (in the sub View) is changed.
For example:
struct ContentView: View {
let items = ["one", "two", "three"]
var body: some View {
let _ = print("---> ContentView recalculated") //<-- for testing
VStack {
TimeView() // <-- only this view is updated
List(items, id: \.self) { item in
Text(item)
}
}
}
}
struct TimeView: View {
@State private var timeNow = ""
let timer = Timer.publish(every: 1, tolerance: 0.5, on: .main, in: .common).autoconnect()
let dateFormatter = DateFormatter()
func updateTime() {
dateFormatter.dateFormat = "LLLL dd, hh:mm:ss a"
timeNow = dateFormatter.string(from: Date())
}
var body: some View {
let _ = print("---> TimeView recalculated") //<-- for testing
Text(timeNow)
.onAppear{ updateTime() }
.onReceive(timer) { _ in
updateTime()
}
}
}
Another example using the same approach:
struct ContentView: View {
let items = ["one", "two", "three"]
@State private var timeNow = ""
var body: some View {
let _ = print("---> ContentView recalculated") //<-- for testing
VStack {
TimeView(timeNow: $timeNow) // <-- only this view is updated
.onAppear{ updateTime() }
.onReceive(timer) { _ in
updateTime()
}
List(items, id: \.self) { item in
Text(item)
}
}
}
let timer = Timer.publish(every: 1, tolerance: 0.5, on: .main, in: .common).autoconnect()
let dateFormatter = DateFormatter()
func updateTime() {
dateFormatter.dateFormat = "LLLL dd, hh:mm:ss a"
timeNow = dateFormatter.string(from: Date())
}
}
struct TimeView: View {
@Binding var timeNow: String
var body: some View {
let _ = print("---> TimeView recalculated") //<-- for testing
Text(timeNow)
}
}