Hiding and unhiding views in SwiftUI

Håvard Fossli
2 min readOct 22, 2020

Trying to hide and unhide views in SwiftUI is not easy. At least not if you want to keep the continuation of state!

Consider this example where we have a CounterView we want to show and hide. SwiftUI makes this very difficult. Its counterstate should probably be a @Binding sent from outside, but nonetheless this should also be easy to do in SwiftUI in my opinion with @State variables.

Steps to reproduce
1. Open app
2. Tap hide
3. Tap show
Expected result
Counter continues from last number
When the view is hidden it gets no accidental onAppear callbacks
Actual result
Counter restarts from 0
When hiding the view a new view is created and it gets an onAppear callback

Solution

In order to work around this issue we need to maintain the state somehow and be able to do something like view.hide(true) without creating a different view tree.

By wrapping the view in a UIKit view we can achieve that:

Original view structure
MyView
New view structure
HideableView (SwiftUI)
-> HideableView.Container (UIView)
-> UIHostingController (UIView)
-> MyView (SwiftUI)

We then get full control over the lifecycle of the view as we can add and remove the UIHostingController from its superview (HideableView.Container).

Try running the example again, but this time using the HideableView we’ve created and with this render function

You’ll see that the counter gracefully continues and is not reset. The onAppear and onDisappear is also working great with the only caveat that we needed to Dispatch.main.async for the first presentation of the view in order to avoid an excessive onAppear, onDisappear callback. Notice this is only true if the content is within a NavigationView, which you likely use anyway.

If you try another solution make sure
- State passed in to your view is updated
- State within your view is in a continuation
- Callbacks for onAppear and onDisappear is sensical and sound

Hope you like it and find it useful!

--

--