Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

focusing search bar in macos NavigationSplitView #397

Unanswered
joprice asked this question in Q&A
Discussion options

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()
 )
Screenshot 2024年01月17日 at 10 42 32 AM
You must be logged in to vote

Replies: 1 comment 2 replies

Comment options

Hey did you ever figure this out?

You must be logged in to vote
2 replies
Comment options

Unfortunately no

Comment options

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 :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
Q&A
Labels
None yet

AltStyle によって変換されたページ (->オリジナル) /