3

How can I stop a tab's scrollview's offset being affect by other tab's offset?

I don't want to force the scroll view to the top every time you show a new tab, but just want the new tabs to be not affected by the scroll position of the last tab I viewed.

import SwiftUI

enum Tab {
    case First, Second, Third
    
    var title: String {
        switch self {
        case .First:
            return "First"
        case .Second:
            return "Second"
        case .Third:
            return "Third"
        }
    }
}

struct ContentView: View {
    @State var selectedTab = Tab.First
    var body: some View {
        NavigationView {
            TabView(selection: $selectedTab) {
                FirstView()
                    .tabItem {
                        Text("First")
                    }.tag(Tab.First)
                SecondView()
                    .tabItem {
                        Text("Second")
                    }.tag(Tab.Second)
                ThirdView()
                    .tabItem {
                        Text("Third")
                    }.tag(Tab.Third)
            }.navigationBarTitle(selectedTab.title, displayMode: .automatic)
            .navigationBarHidden(false)
        }
    }
}

struct FirstView: View {
    let data = [1,2,3,4,5,6,7,8,9,10]
    var body: some View {
        ScrollView(showsIndicators: true) {
            VStack {
                ForEach(data, id: \.self) { item in
                    Text("\(item)")
                        .frame(minWidth: 0, idealWidth: 100, maxWidth: .infinity, minHeight: 0, idealHeight: 100, maxHeight: .infinity, alignment: .center)
                }
            }
        }
    }
}

struct SecondView: View {
    let data = [1,2,3,4,5,6,7,8,9,10]
    var body: some View {
        ScrollView(showsIndicators: true) {
            VStack {
                ForEach(data, id: \.self) { item in
                    Text("\(item)")
                        .frame(minWidth: 0, idealWidth: 100, maxWidth: .infinity, minHeight: 0, idealHeight: 100, maxHeight: .infinity, alignment: .center)
                }
            }
        }
    }
}

struct ThirdView: View {
    let data = [1,2,3,4,5,6,7,8,9,10]
    var body: some View {
        ScrollView(showsIndicators: true) {
            VStack {
                ForEach(data, id: \.self) { item in
                    Text("\(item)")
                        .frame(minWidth: 0, idealWidth: 100, maxWidth: .infinity, minHeight: 0, idealHeight: 100, maxHeight: .infinity, alignment: .center)
                }
            }
        }
    }
}
daihovey
  • 3,485
  • 13
  • 66
  • 110
  • So do you want to restore the state of how far is scrolled? I'm a bit confused, some more description would be useful. Also "just want the new tabs to be affected by the last" doesn't make sense to me either. – George Oct 05 '20 at 00:31
  • sorry, typo "just want the new tabs to be NOT be affected by the last" – daihovey Oct 05 '20 at 04:58
  • I'm still confused whether you want to start each new opened tab from the top, or from the scroll position of the last tab opened. The sentence seems a bit contradictory. – George Oct 05 '20 at 11:46
  • Ok I think I get it - the navigation bar changes form based on the last tab viewed? – George Oct 05 '20 at 11:49

1 Answers1

2

It is because you use one NavigationView, so it preserves own state. Make NavigationView independent for each tab.

Tested with Xcode 12 / iOS 14

demo

struct ContentView: View {
    @State var selectedTab = Tab.First
    var body: some View {
        TabView(selection: $selectedTab) {
            NavigationView {
                FirstView()
                    .navigationBarTitle(Tab.First.title)
            }
            .tabItem {
                Text("First")
            }.tag(Tab.First)
            NavigationView {
                SecondView()
                    .navigationBarTitle(Tab.Second.title)
            }
            .tabItem {
                Text("Second")
            }.tag(Tab.Second)
            NavigationView {
                ThirdView()
                    .navigationBarTitle(Tab.Third.title)
            }
            .tabItem {
                Text("Third")
            }.tag(Tab.Third)
        }
    }
}
Asperi
  • 228,894
  • 20
  • 464
  • 690
  • 1
    The reason why I had the NavigationView wrapping the TabView is because I wanted to hide the TabBar when I pushed a detail view from say FirstView. The above solution doesnt hide the tabbar. Any idea how to achieve both, the scrollview content issue and the hiding the tabbar when NavigationLink-ing to a detail view – daihovey Oct 06 '20 at 04:35
  • 1
    It is not presented anyhow in original question... anyway, the following my answer might be helpful https://stackoverflow.com/a/61971653/12299030 – Asperi Oct 06 '20 at 09:55