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
SearchBar. This is a bit more cleaner and we can style the search field and the list independently.
onChange is now added to the new
SearchBar. The value is propagated thru the
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
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
Intermezzo: Change List to
Before we continue to style and implementing the search bar behavior we should consider changing
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.
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
ForEach inside a
LazyVStack. Then we wrap the
LazyVStack with a
onAppear is now added to the
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
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
true and hiding the
And we have to set
true if the user taps on the search field using
onTapGesture and turn it to false using the
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
As you see, now it works as it should.
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
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.
To solve this, let’s add a
transition to the
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
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
Important: Before this will take effect, we have to be sure, that the
Array is long enough. So we need to add some more Fruits.