简体   繁体   English

在视图加载时滚动到滚动视图中选定的 position - SwiftUI

[英]Scroll to selected position in scrollview when view loads - SwiftUI

OUTLINE大纲

I have 2 views, the first (view1) contains a HStack and an @ObservableObject .我有 2 个视图,第一个 (view1) 包含一个HStack和一个@ObservableObject When the user selects a row from the HStack the @ObservableObject is updated to the string name of the row selected.当用户从HStack中选择一行时,@ @ObservableObject将更新为所选行的字符串名称。

In view2 I have the same HStack as the first HStack in view1.在 view2 中,我的HStack与 view1 中的第一个HStack相同。 This HStack observes @ObservableObject and desaturates all other rows except the one that matches the @ObservableObject .HStack观察@ObservableObject并对除与@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. view2 中的HStack列表比页面宽,所以我想在视图出现时自动滚动到饱和/选定的行。 I'm not totally sure how to use ScrollTo as it needs an integer and I am only storing/observing the string name.我不完全确定如何使用ScrollTo ,因为它需要 integer 并且我只是存储/观察字符串名称。

VIEW 1查看 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:视图2:

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.您可以将ScrollViewReader与在ForEach中应用的id一起使用,因此您实际上不需要行索引号(尽管如果需要,也可以通过使用enumerated或搜索applications数组来获得为项目的索引。

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我刚刚制作了一个基本的 ContentView 来显示这两个视图,因为我不确定它们是如何为您布置的
  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. ContentView 拥有SelectedApplicationState (这是您的selectionApplication ObservableObject (顺便说一下,将您的类型名称大写是一种常见的做法 - 这就是我更改名称的原因。另外,具有此类的类型和属性令人困惑类似的名称)并将其传递给两个视图。
  3. SelectedApplicationState now holds the applications array, since it was being duplicated across views anyway SelectedApplicationState现在保存applications数组,因为无论如何它都在视图中被复制
  4. On selection in View1, selectedApplication in the ObservableObject is set, triggering onReceive in View2在 View1 中selectedApplication时,设置 ObservableObject 中的 selectedApplication,在View2中触发onReceive
  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在那里, ScrollViewReader被告知滚动到 id 存储在selectedApplication中的项目,该项目作为app传递给onReceive闭包

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.如果这些视图位于不同的页面上,则 View2 的 position 在导航到后仍将正确设置,因为onReceive将在首次加载时触发并将其设置为正确的 position。 The only requirement is passing that instance of SelectedApplicationState around.唯一的要求是传递SelectedApplicationState的实例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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