loading...

Replicating the MacOS Search TextField in SwiftUI

mitchartemis profile image Mitch Stanley Originally published at fullstackstanley.com ・2 min read

I wanted to add a search text field to a MacOS app that I'm working on and soon discovered it's not available in SwiftUI (as off 5.1). I've put together a quick replication which mostly works, but unfortunately involves some hackiness.

Preview of Search TextFIeld

Here's the code:

// This extension removes the focus ring entirely.
extension NSTextField {
    open override var focusRingType: NSFocusRingType {
        get { .none }
        set { }
    }
}

struct SearchTextField: View {
    @Binding var query: String
    @State var isFocused: Bool = false
    var placeholder: String = "Search..."
    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: 5, style: .continuous)
            .fill(Color.white)
            .frame(width: 200, height: 22)
                .overlay(
                    RoundedRectangle(cornerRadius: 5, style: .continuous)
                        .stroke(isFocused ? Color.blue.opacity(0.7) : Color.gray.opacity(0.4), lineWidth: isFocused ? 3 : 1)
                        .frame(width: 200, height: 21)
            )

            HStack {
                Image("magnifyingglass").resizable().aspectRatio(contentMode: .fill)
                    .frame(width:12, height: 12)
                    .padding(.leading, 5)
                    .opacity(0.8)
                TextField(placeholder, text: $query, onEditingChanged: { (editingChanged) in
                    if editingChanged {
                        self.isFocused = true
                    } else {
                        self.isFocused = false
                    }
                })
                    .textFieldStyle(PlainTextFieldStyle())
                if query != "" {
                    Button(action: {
                            self.query = ""
                    }) {
                        Image("xmark.circle.fill")
                            .resizable()
                            .aspectRatio(contentMode: .fit)
                            .frame(width:14, height: 14)
                            .padding(.trailing, 3)
                            .opacity(0.5)
                    }
                    .buttonStyle(PlainButtonStyle())
                    .opacity(self.query == "" ? 0 : 0.5)
                }
            }

        }
    }
}

Here's how you use it:

SearchTextField(query: $searchQuery, placeholder: "Search...")
  .frame(minWidth: 60.0, idealWidth: 200.0, maxWidth: 200.0, minHeight: 24.0, idealHeight: 21.0, maxHeight: 21.0, alignment: .center)

Caveats

There are a few issues with this solution. Hopefully in a future version of SwiftUI this will be fixed.

Removing the Focus Ring from All TextFields

This code creates a pretend TextField which holds the magnifying glass, a real TextField, and the close icon inside of it. If you remove the NSTextField extension you'll see the real textfield when focused. It's currently not possible to disable a specific focus ring so you'll have to reimplement them if you need to show them.

Fake Focus Ring Doesn't Update on Focus

You'll notice in the GIF above that the focus ring does not show until you start typing. Not ideal but usable. If anyone has a suggestion for fixing this I'd be happy to know!

No SF Symbols in MacOS

Surprisingly, MacOS does not have native support for SF Symbols! I used this tool to convert them to PNG and import them into my assets catalog. The symbols used are xmark.circle.fill and magnifyingglass.

Posted on by:

mitchartemis profile

Mitch Stanley

@mitchartemis

Developer of Snipline and other cool stuff.

Discussion

pic
Editor guide