TabView reload all content constantly

Check example TabView project ( xcode starter TabView project ) - make NavigationView, List and so on in one tab, and for example Text("hello") for other tab.

Scroll list or/and navigate some screen in first tab, then goto second tab. And finally when open first tab again - view will fully reloaded ( List in 0 position, navigation reseted, But state properties will saved )


Check standard native Contact app. Switching over tabs - save ALL context was created. Scroll down contacts list than switch tab and switch again to contacts tab - scroll was the same ( context saved )

Replies

Copuld you show YOUR code ?

I can consistently reproduce this behavior with the following code:


import SwiftUI

struct ContentView: View {
    @State private var selection = 0

    var body: some View {
        TabView(selection: $selection){
            FirstTabView()
                .tabItem {
                    VStack {
                        Image("first")
                        Text("First")
                    }
                }
                .tag(0)
            SecondTabView()
                .font(.title)
                .tabItem {
                    VStack {
                        Image("second")
                        Text("Second")
                    }
                }
                .tag(1)
        }
    }
}

struct FirstTabView: View {
    var body: some View {
        List {
            ForEach(1...50, id: \.self) { num in
                Text("Row \(num)")
            }
        }
    }
}

struct SecondTabView: View {
    var body: some View {
        Text("Second View")
    }
}


I'd suggest filing a bug. It seems that a layout occurs on the table view at the point it's taken offscreen (you'll see a printout from UITableView in the debugger output from the simulator), and that may be what's causing it to reset—the size may have gone to zero or become invalid, taking the scroll offset with it. This can probably be worked around with a little Anchor and PreferenceKey magic, but you'd normally not be doing such things unless you wanted to, say, have the table always snap into place at a cell division, which is not terribly likely.


There's an outside possibility that this has something to do with SwiftUI's layout model; it may be discarding and recreating the views. I somewhat doubt that though—there should be a real UITabController there, holding onto its content.

Okay, looks like this is entirely down to SwiftUI being weird. It appears to be gaming the UITabBar system. Each time you tap to select a tab, SwiftUI is changing the view controller list for the tab bar. For n tabs, it contains n-1 plain UIViewControllers with empty views, and 1 HostingController<_ViewList_View>. Tapping on a tab will update the content of hosting controller to contain the content of the SwiftUI tab, and it will reassign the UITabBarController's viewControllers property—it moves the HostingController to the index of the tapped tab, and puts a new empty UIViewController at its old position.


Here's the output of that property as I walk through four SwiftUI tabs:


(lldb) po [$21 viewControllers]
<__NSArrayM 0x6000015091d0>(
<_TtGC7SwiftUI19UIHostingControllerVS_14_ViewList_View_: 0x7fddabe0c600>,
<UIViewController: 0x7fdd9bc2eeb0>,
<UIViewController: 0x7fddabf2bd80>,
<UIViewController: 0x7fddabd11b40>
)

(lldb) po [$21 viewControllers]
<__NSArrayM 0x6000015087b0>(
<UIViewController: 0x7fddabd27170>,
<_TtGC7SwiftUI19UIHostingControllerVS_14_ViewList_View_: 0x7fddabe0c600>,
<UIViewController: 0x7fddabf2bd80>,
<UIViewController: 0x7fddabd11b40>
)

(lldb) po [$21 viewControllers]
<__NSArrayM 0x600001504780>(
<UIViewController: 0x7fddabd27170>,
<UIViewController: 0x7fddabd0a9a0>,
<_TtGC7SwiftUI19UIHostingControllerVS_14_ViewList_View_: 0x7fddabe0c600>,
<UIViewController: 0x7fddabd11b40>
)

(lldb) po [$21 viewControllers]
<__NSArrayM 0x6000015083c0>(
<UIViewController: 0x7fddabd27170>,
<UIViewController: 0x7fddabd0a9a0>,
<UIViewController: 0x7fdd9bc3f830>,
<_TtGC7SwiftUI19UIHostingControllerVS_14_ViewList_View_: 0x7fddabe0c600>
)


As you can see, the same UIHostingController<_ViewList_View> is making its way to the index of the current tab, and a new blank controller is being put in its place. With this behavior, it's no surprise that your List view is always being reset—with no good way of recording your scroll offset (or setting it) via your own @State properties, there's no way to persist that information across updates of the view hierarchy.


Please file a bug.

> UITabBar system ~ persist that information across updates of the view hierarchy.


Speaking of which - have you seen the docs on 'order of containment*? If you're in line with those rules (it seems maybe you are), then I'd be tempted to echo SwiftUI's acting out...if not, then all bets are off and I'm tempted instead to (ouch) blame the victim.


*From the docs on 'Order of Containment': https://developer.apple.com/library/ios/documentation/WindowsViews/Conceptual/ViewControllerCatalog/Chapters/CombiningViewControllers.html


-=-

Combined View Controller Interfaces

You can use the view controllers that the UIKit framework provides by themselves or in conjunction with other view controllers to create even more sophisticated interfaces. When combining view controllers, however, the order of containment is important; only certain arrangements are valid. The order of containment, from child to parent, is as follows:

  • Content view controllers, and container view controllers that have flexible bounds (such as the page view controller)
  • Navigation view controller
  • Tab bar controller
  • Split view controller

Is there a fix for this problem yet?

Also met this issue. I have TabView, and the first tab - WebView. It reloads every time another tab being opened... Weird.

Still no solution?
Hi,

I don't know if it's a good idea, but it solves de problem of constant reloading in my project.

Code Block
struct NavigationLazyView<Content: View>: View {
    let build: () -> Content
    init(_ build: @autoclosure @escaping () -> Content) {
        self.build = build
    }
    
    var body: Content {
        build()
    }
}


Code Block
var body: someView {
TabView(selection: $iSelectedTab) {
                NavigationLazyView(RestaurantManagmentDashboardHomeView(presenter: RestaurantManagmentDashboardHomePresenter())).tabItem {
                    Image("Home_Selected")
                    Text("")
                }.tag(0)
            
                NavigationLazyView(RestaurantManagmentDashboardObjectInventoryRouter.assembleModule()).tabItem {
                    Image("List_Selected-1")
                    Text("")
                 }.tag(1)
            }
}