-
Notifications
You must be signed in to change notification settings - Fork 389
-
I'm trying to add a search keyboard shortcut to the search bar in a NavigationSplitView, but found an edge case that I think Introspect should be able to help with: The code below works to trigger the search view when the input has no value (isSearching = false
), but the shortcut does not work after that to bring focus back to the search bar, since isSearching
seems to remain true when the input contains data. I haven't seen a method for associating FocusState with searchable
, so I looked for an introspect modified that might help. I found that the searchField
modified does not have an implementation for Mac. I ran a capture view hierarchy to see if I could add a local modifier, but wasn't able to figure out where the AppKitSearchField class in the attached screenshot should map to, as I couldn't resolve it. Is it possible to add support to Introspect for this?
.searchable(text: $filter, isPresented: $isSearching, placement: .sidebar) .background( Button("", action: { isSearching = true }) .keyboardShortcut("f").hidden() )
Beta Was this translation helpful? Give feedback.
All reactions
Replies: 1 comment 2 replies
-
Hey did you ever figure this out?
Beta Was this translation helpful? Give feedback.
All reactions
-
Unfortunately no
Beta Was this translation helpful? Give feedback.
All reactions
-
Well I couldn't either so I ditched introspect and played around in the debugger with the view hierarchy and found 2 possible solutions. This first method won't work for you because your search field is in the sidebar but method 2 will work in that case. Or you can modify method 1 and make that work too.
My case was slightly different as I needed to activate the search in the toolbar placement after a user searched presses enter and then presses cmd+f again isSearching = true
would no longer work. I used this function and called it in the cmd+f button action
Method 1
func focusSearchField() { NSApp.keyWindow?.toolbar?.items.forEach { if let searchField = 0ドル as? NSSearchToolbarItem { searchField.beginSearchInteraction() } } } func resignSearchField() { NSApp.keyWindow?.toolbar?.items.forEach { if let searchField = 0ドル as? NSSearchToolbarItem { searchField.endSearchInteraction() } } }
Or also to unfocus you can use just simply NSApp.keyWindow?.makeFirstResponder(nil)
Method 2
Now the alternate method would be to traverse the hierarchy of views and get the underlying NSSearchField and do the same thing basically
Attach search field sniffer to ContentView where your searchable modifier lives
.searchable(text: $filter, isPresented: $isSearching, prompt: "Search location") .background(SearchFieldSniffer())
SearchFieldSniffer
struct SearchFieldSniffer: NSViewRepresentable { func makeNSView(context: Context) -> NSView { DispatchQueue.main.async { if let field = findSearchField(in: NSApp.keyWindow) { SearchCoordinator.shared.searchField = field } } return NSView() } func updateNSView(_ nsView: NSView, context: Context) {} private func findSearchField(in window: NSWindow?) -> NSSearchField? { guard let views = window?.contentView?.superview?.subviews else { return nil } for view in views { if let searchField = searchFieldInView(view) { return searchField } } return nil } private func searchFieldInView(_ view: NSView) -> NSSearchField? { if let field = view as? NSSearchField { return field } for subview in view.subviews { if let field = searchFieldInView(subview) { return field } } return nil } }
SearchCoordinator
class SearchCoordinator: ObservableObject { static let shared = SearchCoordinator() weak var searchField: NSSearchField? func focus() { if let field = searchField { NSApp.keyWindow?.makeFirstResponder(field) } } func resign() { NSApp.keyWindow?.makeFirstResponder(nil) } }
Now you can just call SearchCoordinator.shared.focus()
in your button action cmd+f
Hope this helps :)
Beta Was this translation helpful? Give feedback.