I noticed that Navigationbar- / Toolbar Buttons are not working properly when there is at least one @State variable refreshing the View very often.
I created a simple
The number
state in provided variant refreshes entire view, that is a result of issue, and not very optimal for UI.
The solution for navigation bar, and in general good style, is to separate refreshing part into standalone view, so SwiftUI rendering engine rebuild only it.
Tested with Xcode 12b3 / iOS 14
struct TestOftenUpdate: View {
@State private var showModal = false
var body: some View {
NavigationView {
VStack {
QuickTimerView()
Button(action: { showModal.toggle() }, label: {
Text("Open Modal")
})
}
.navigationBarTitle("Home", displayMode: .inline)
.navigationBarItems(leading:
Button(action: {
showModal.toggle()
}, label: {
Text("Open Modal")
}))
.toolbar {
ToolbarItem(placement: .bottomBar) {
Button(action: {
showModal.toggle()
}, label: {
Text("Open Modal")
})
}
}
}
.sheet(isPresented: $showModal, content: {
Text("Hello, World!")
})
}
}
struct QuickTimerView: View {
@State private var number = 0
var body: some View {
VStack {
Text("Count \(number)")
Button(action: startTimer, label: {
Text("Start Timer")
})
}
}
func startTimer() {
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { (_) in
number += 1
}
}
}
With toolbar
is different issue - it does not work after first sheet open even without timer view, and here is why
as it is seen the button layout has corrupted and outside of toolbar area, that is why hit testing fails - and this is Apple defect that should be reported.
Workaround 1:
Place toolbar outside navigation view (visually toolbar is smaller, but sometimes might be appropriate, because button works)
NavigationView {
// ... other code
}
.toolbar {
ToolbarItem(placement: .bottomBar) {
Button(action: {
showModal.toggle()
}, label: {
Text("Open Modal")
})
}
}