How to maintain scroll position in a SwiftUI TabView
Unfortunately this is simply not possible with built-in components given the current limitations of SwiftUI (iOS 13.x/Xcode 11.x).
Two reasons:
- SwiftUI completely disposes of your View when you switch away from the tab. This is why your scroll position is lost. It's not like UIKit where you have a bunch of offscreen UIViewControllers.
- There is no
ScrollView
equivalent ofUIScrollView.contentOffset
. That means you can't save your scroll state somewhere and restore it when the user returns to the tab.
Your easiest route is probably going to be using a UITabBarController
filled with UIHostingController
s. That way you won't lose the state of each tab as users move between them.
Otherwise, you could create a custom tab container, and modify the opacity of each tab yourself (as opposed to conditionally including them in the hierarchy, which is what causes your problem currently).
var body: some View {
ZStack {
self.tab1Content.opacity(self.currentTab == .tab1 ? 1.0 : 0.0)
self.tab2Content.opacity(self.currentTab == .tab2 ? 1.0 : 0.0)
self.tab3Content.opacity(self.currentTab == .tab3 ? 1.0 : 0.0)
}
}
I've used this technique when I wanted to keep a WKWebView
from completely reloading every time a user briefly tabbed away.
I would recommend the first technique. Especially if this is your app's main navigation.
November 2020 update
TabView
will now maintain scroll position in tabs when moving in between tabs, so the approaches in the answers to my original question are no longer necessary.