SwiftUI: A custom reactive TabBar
Note 19-mar-2020: In the meantime, I went much deeper into the question how a scrollable tab view with animations can be realized using pure SwiftUI. I came up with a solution that is published on GitHub.
Please also note that there is a follow-up article on this one.
SwiftUI provides a TabView
. However, checking for the currently selected tab, and reordering of the tabs lead to problems if you want, as an example, save and restore the new sort order on app restart.
So, I tried to design a TabBar which can be used pretty conveniently as replacement for the original TabView
.
Wrapping the UITabBarController
First, we’re wrapping the UITabBarController
into a UIViewControllerRepresentable.
The procedure is explained in various tutorials across the web, so I’ll focus on some additional aspects here.
- This is the index that we want to have maintained by the
UITabBarController
. - Similarily, this is the order in which the tabs shall appear.
- We set the
Coordinator
as delegate for theUITabBarController
. This allows us to react on the events. - From the given list of
IRTabBarItem
s, we create the list of view controllers, as well as the attachedUITabBarItem
s, that we need for theUITabBarController
. - If we have a
tabOrder
available, we sort those list of view controllers accordingly. - We finish the setup of the
tabBarController
.
Reacting on events
Next, we need the Coordinator
. Its job is simply to react on the events we’ll notice.
- We save the parent
struct
just for convenience. This avoids some castings lateron. - We take the
selectedIndex
and save it to the parent’s@Published
property. - The same is true for the new sort order.
Defining the tab bar item
Now, let’s define an IRTabBarItem
, so that we can provide title, image and target views to the tab bar.
Using the new view
We can now use the view like any other SwiftUI view:
- We use a model to provide all we need for the view. We’ll see that in a moment.
- That’s our new
IRTabBar
with the items provided by the model, and the selected index and the tab order bound to the model’s properties.
The model provides the convenience
Finally, we have to implement the model that the view observes.
- Those two are custom property wrappers that read and write values from/to the user defaults.
- These are the observed properties; if set, the user defaults are updated as well. (I’m sure this can be done nicer…)
- The preset for the tab bar items we want to present. Derived from an enumeration which provides
title, systemImageName, view
for each case. - This reads the saved settings and sets the published properties accordingly.
Conclusion
With this view, your have a pretty easy way to add a reactive TabView
to your app. If you need additional features, such as transitioning animations, you can easily add them to the Coordinator
. Or, you can even publish the items and set them dynamically in. the model, e.g., if you want to show or hide tabs depending on other conditions.