A SwiftUI Search Bar — Part 2

Write a SwiftUI search bar just in plain SwiftUI without wrapping UIKit components like UISearchBar.

Sören Kirchner
5 min readFeb 7, 2021
Photo by Chase Clark on Unsplash

Previous Part: A SwiftUI search bar — Part 1

Now let’s style the search bar and add some feature to it.

  • a common search bar like look
  • an animated cancel-button
  • hidden on start and shown on wipe down

The contact list of the Signal messenger app serves as a model.

Create a SearchBar View

First, let’s separate the Textfield into its own View called SearchBar. This is a bit more cleaner and we can style the search field and the list independently.

Note: onChange is now added to the new SearchBar. The value is propagated thru the @Binding.

Style the Search Bar

Let’s now style the search bar to a more common one. Therefore we put a magnifying glass Image left next to the Textfield by putting both into a HStack. And then we lay the HStack over a RoundedRectangle.

Voila. It now looks like a common search bar. At the moment, the search bar is a little sticky to the borders, but we will solve it later.

Add a Cancel Button

Now we add a cancel button by wrapping it all up in another HStack. Then we put a Button right next to the just now styled TextField.

Intermezzo: Change List to ScrollView and LazyVStack

Before we continue to style and implementing the search bar behavior we should consider changing List to ScrollView and LazyVStack. This comes with advantages and with disadvantages.

The Disadvantages. We lose some List features like the default swipe to delete action and we have to implement this feature by ourselves if it’s needed.

The Advantages. List is limited to some Styles you can choose. The default style comes with these separator lines and you cannot easily disable them (on iOS14). And this style highlights the cell if you click on a button inside. A LazyVStack does not have such limitations. You are free to style it for your needs.

Let’s replace the List with a ScrollView and a LazyVStack. Therefore we place the SearchBar and the Elements of our List using ForEach inside a LazyVStack. Then we wrap the LazyVStack with a ScrollView.

Note: onAppear is now added to the LazyVStack!

Now the separator lines are gone and the search bar doesn’t stick to the borders anymore, because it’s now inside of the ScrollView and the ScrollView got padding.

Show and Hide the Cancel-Button

Like in the Signal messenger app, the search field is always visible on top of the list, but the cancel-button will only be shown if the user taps on the search field and will disappear if the user is done.

We add a new @State property showCancelButton to the SearchBar that will store if the cancel-button is visible or not.

We then wrap the Button in an if condition to show the Button if showCancelButton is true and hiding the Button otherwise.

And we have to set showCancelButton to true if the user taps on the search field using onTapGesture and turn it to false using the Buttons action parameter.

And of course, we have to empty the search field by setting text to an empty String. This will automatically call the FruitsController search method which then updates the publishedFruits to all fruits.

There is a little problem left over. After clicking on the cancel-button, the search field is still focused and the keyboard doesn’t disappear. Unfortunately, there is no built-in SwiftUI method to hide the keyboard and to clear the focus. But there is a solution. So let’s add the hideKeyboard method as an extension to the View class.

Now we can add the method to the action of the cancel-button in the SearchBar View.

As you see, now it works as it should.

Add Animation

To give the search bar a more sophisticated look and feel, let’s animate the appearance of the cancel-button a little. And this is (mainly) easy with SwiftUI. Every time we change the state of showCancelButton in our code we have to wrap it up inside a withAnimation block.

It’s not bad, but if you take a closer look, it could be better. At the moment, if the button fades out it intersects with the moving bar.

Add Transition

To solve this, let’s add a transition to the Button. A transition to the right (trailing) edge.

It looks now as wanted.

Hide the Search Bar on Long Lists

And finally we want to hide the search bar on the appearance of the FruitsView if the list is longer than the screen like in the Signal messenger app.

To do this we simply try to scroll the list to the first element of our publishedFruits Array which is the second element of our List. (The first is the SearchBar)

Therefore let’s wrap the LazyVStack with a ScrollViewReader. Then we can scroll to the first element of our Array when the LazyVStack appears using the proxy of the ScrollViewReader.

Important: Before this will take effect, we have to be sure, that the publishedFruits Array is long enough. So we need to add some more Fruits.

The result

Source Code: https://github.com/soeren-kirchner/SearchBarSwiftUI-Part2