I'm building a macOS SwiftUI app. In it, I have a popover with a TextField. The TextField has a binding that updates an ObservableObject that then propagates its value back down a NavigationView to the Detail View.
On the first keyboard entry to the TextField, the popover closes. However, on all subsequent entries, the popover stays open.
I've also tried this with a DatePicker and have had the same results, so it's not limited to the TextField entry.
Note that this does not happen when running the same code on iOS (on iPhone where popovers are displayed as sheets or on iPad where they are popovers).
I've included a minimal example.
Anyone have any clue how to avoid the popover closing on first edit?
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
var window: NSWindow!
func applicationDidFinishLaunching(_ aNotification: Notification) {
let contentView = ContentView(store: AppState())
window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
backing: .buffered, defer: false)
window.center()
window.setFrameAutosaveName("Main Window")
window.contentView = NSHostingView(rootView: contentView)
window.makeKeyAndOrderFront(nil)
}
}
class AppState : ObservableObject {
@Published var models : [String:Model] = ["1" : Model(id: "1", title: "June 14"),
"2" : Model(id: "2", title: "July 21"),
"3" : Model(id: "3", title: "Sept 5")]
func changeTitle(key: String, newTitle: String) {
guard var model = self.models[key] else {
return
}
model.title = newTitle
self.models[key] = model
}
}
struct Model {
var id : String
var title: String
}
struct ContentView: View {
@ObservedObject var store : AppState
var body: some View {
NavigationView {
VStack {
List {
ForEach(Array(store.models.values), id: \.id) { model in
NavigationLink(destination: DetailView(model: model,
changeTitle: { title in
self.store.changeTitle(key: model.id, newTitle: title)
})) {
VStack(alignment: .leading) {
Text(model.title).font(.headline)
}.padding(.vertical, 8)
}
}
}.frame(minWidth: 150, idealWidth: 200, maxWidth: 200, maxHeight: .infinity)
}
EmptyView()
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
}
struct DetailView : View {
var model : Model
var changeTitle: (String) -> Void
@State private var showPopover = false
var customBinding : Binding<String> {
Binding<String>(get: { self.model.title }, set: { newValue in
self.changeTitle(newValue)
})
}
var body: some View {
VStack {
Text(model.title)
Button("Edit") {
self.showPopover.toggle()
}.popover(isPresented: self.$showPopover) {
TextField("Text", text: self.customBinding)
.frame(minWidth: 300)
.padding()
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}