Search code examples
swiftuiwatchos

How to reset the toolbar back to its original position


I'm trying to setup a toolbar in watchos which displays a button when I scroll the view down. Everything works, the button scrolls down and I can navigate to another page. When returning however, I would like the scrollview to be in the same position as when the app loads, so without the button being visible.

The code I have now is:

struct ContentView: View {
    
    @State private var selectedPage: String? = nil
    
    var body: some View {
        ScrollView {
            VStack(alignment: .leading) {
                ForEach(0..<100) {
                    Text("Row \($0)")
                }
            }
            .toolbar {
                ToolbarItem(placement: .primaryAction) {
                    Button("Settings") {
                        selectedPage = "Settings"
                    }
                }
            }
            .background(
                NavigationLink(destination: SettingsView(), tag: "Settings",selection: $selectedPage) {}
                    .hidden()
            )
            .navigationTitle {
                Text("Navigation")
            }
        }
    }
}

I Have tried using a scrollViewReader, but think I'm looking into the wrong directing as it allows the scrollView to go to a certain position, but the toolbar seems to be no part of it and stays in view. When reading the scrollviews offset position (not in code but check gif), the offset is 0 when the button is not visible (initial state). When scrolling down the offset goes up, but when scrolling up until the button becomes visible the offset of the scrollview is 0 as well.

preview

struct ContentView: View {
    
    @State private var selectedPage: String? = nil
    
    var body: some View {
        ScrollView {
            ScrollViewReader { reader in
                VStack(alignment: .leading) {
                    ForEach(0..<100) { i in
                        Text("Row \(i)")
                            .id(i)
                    }
                    .toolbar {
                        ToolbarItem(placement: .primaryAction) {
                            Button("Settings") {
                                selectedPage = "Settings"
                            }
                        }
                    }
                    .background(
                        NavigationLink(destination: SettingsView(), tag: "Settings",selection: $selectedPage) {}
                            .hidden()
                    )
                    .onAppear {
                        withAnimation {
                            reader.scrollTo(0, anchor: .top)
                        }
                    }
                    .navigationTitle {
                        Text("Navigation")
                    }
                }
            }
        }
    }
}

When choosing 1 as value to scroll to the button is pushed back to the top and in the list id 1 is selected.

.onAppear {
    withAnimation {
    reader.scrollTo(1, anchor: .top)
    }
}

So it's working with id 1 and higher, but when using id 0 the view is not reset.

So how to reset the view to the initial state with the button being hidden on top ?


Solution

  • Solved it simply by adding a @State property which is true when loading the view and which changes to false if navigated away from the view and then conditionally show the item within the .toolbar view modifier.

    @State var loadedMainView = true
    
    var body: some view {
        ScrollView {
            // .... code
        }
        .toolbar {
            ToolbarItem {
                if loadedMainView {
                    Button("My Button") {
                    }
                }
            }
        .onAppear {
            loadedMainView = true
        }
        .onDissappear {
            loadedMainView.toggle()      
    }