简体   繁体   中英

Scroll to selected position in scrollview when view loads - SwiftUI

OUTLINE

I have 2 views, the first (view1) contains a HStack and an @ObservableObject . When the user selects a row from the HStack the @ObservableObject is updated to the string name of the row selected.

In view2 I have the same HStack as the first HStack in view1. This HStack observes @ObservableObject and desaturates all other rows except the one that matches the @ObservableObject .

PROBLEM

The HStack list in view2 is wider than the page so I would like to automatically scroll to the saturated/selected row when the view appears. I'm not totally sure how to use ScrollTo as it needs an integer and I am only storing/observing the string name.

VIEW 1

class selectedApplication: ObservableObject {
    @Published var selectedApplication = "application1"
}

struct view1: View {
    
    @ObservedObject var selectedOption = selectedApplication()

    var applications = ["application1", "application2", "application3", "application4", "application5", "application6", "application7", "application8", "application9", "application10"]
    
    var body: some View {
        VStack{
            ScrollView(.horizontal){
                HStack{
                    ForEach(applications, id: \.self) { item in
                        Button(action: {
                            self.selectedOption.selectedApplication = item
                        }) {
                            VStack(alignment: .center){
                                Text(item)
                            }
                        }
                    }
                }
            }
        }
    }
}

View2:

struct View2: View {
    
    @ObservedObject var application: selectedApplication
    
    var applications = ["application1", "application2", "application3", "application4", "application5", "application6", "application7", "application8", "application9", "application10"]
    
    var body: some View {
        HStack{
            ScrollView(.horizontal, showsIndicators: false) {
                ScrollViewReader{ scroll in
                    HStack{
                        ForEach(applications, id: \.self) { item in
                            Button(action: {
                                application.selectedApplication = item
                            }) {
                                Text(item)
                                    .saturation(application.selectedApplication == item ? 1.0 : 0.05)
                            }
                            
                        }
                    }
                }
            }
        }
    }
}

You can use ScrollViewReader with the id that's being applied in the ForEach , so you don't actually need the row index number (although that, too, is possible to get, if you needed to, either by using enumerated or searching the applications array for the index of the item.

Here's my updated code:

struct ContentView : View {
    @ObservedObject var selectedOption = SelectedApplicationState()
    
    var body: some View {
        VStack {
            View1(application: selectedOption)
            View2(application: selectedOption)
        }
    }
}

class SelectedApplicationState: ObservableObject {
    @Published var selectedApplication = "application1"
    
    var applications = ["application1", "application2", "application3", "application4", "application5", "application6", "application7", "application8", "application9", "application10"]
}

struct View1: View {
    @ObservedObject var application: SelectedApplicationState

    var body: some View {
        VStack{
            ScrollView(.horizontal){
                HStack{
                    ForEach(application.applications, id: \.self) { item in
                        Button(action: {
                            self.application.selectedApplication = item
                        }) {
                            VStack(alignment: .center){
                                Text(item)
                            }
                        }
                    }
                }
            }
        }
    }
}

struct View2: View {
    
    @ObservedObject var application: SelectedApplicationState
    
    var body: some View {
        HStack{
            ScrollView(.horizontal, showsIndicators: false) {
                ScrollViewReader{ scroll in
                    HStack{
                        ForEach(application.applications, id: \.self) { item in
                            Button(action: {
                                application.selectedApplication = item
                            }) {
                                Text(item)
                                    .saturation(application.selectedApplication == item ? 1.0 : 0.05)
                            }
                        }
                    }.onReceive(application.$selectedApplication) { (app) in
                        withAnimation {
                            scroll.scrollTo(app, anchor: .leading)
                        }
                    }
                }
            }
        }
    }
}

How it works:

  1. I just made a basic ContentView to show the two views, since I wasn't sure how they're laid out for you
  2. ContentView owns the SelectedApplicationState (which was your selectionApplication ObservableObject (by the way, it is common practice to capitalize your type names -- that's why I changed the name. Plus, it was confusing to have a type and a property of that type with such a similar name) and passes it to both views.
  3. SelectedApplicationState now holds the applications array, since it was being duplicated across views anyway
  4. On selection in View1, selectedApplication in the ObservableObject is set, triggering onReceive in View2
  5. There, the ScrollViewReader is told to scroll to the item with the id stored in selectedApplication , which is passed to the onReceive closure as app

In the event that these views are on separate pages, the position of View2 will still get set correctly once it is navigated to, because onReceive will fire on first load and set it to the correct position. The only requirement is passing that instance of SelectedApplicationState around.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM