SwiftUI 使用 Swift 结构和 ID 时让 ScrollViewReader 滚动

SwiftUI getting ScrollViewReader to scroll when using Swift structures and id's

我有几个简单的 Swift UI 屏幕结构的所有 运行。该结构定义了一个小部件的视图、它的名称和它进入的顺序。我正在尝试制作一个水平列表,其中每个按钮在按下时都会在滚动视图中居中。这就是我想要做的:

我遇到的问题是我无法让 ScrollViewReader 将 scoll 视图移动到正确的位置。它认为这与 ids 有关,但我不确定。我是 SwiftUI 的新手,这可能是一个非常基本的问题,但非常感谢您的帮助!

这是我的结构

struct WidgetView: Identifiable, Hashable{
    static func == (lhs: WidgetView, rhs: WidgetView) -> Bool {
        return lhs.id == rhs.id && lhs.friendlyName == rhs.friendlyName && lhs.order == rhs.order && lhs.selected == rhs.selected
    }
    
    let id = UUID()
    var friendlyName: String
    var view: Any
    var order: Int
    var selected: Bool
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }
}

extension WidgetView {
    static var data: [WidgetView] {
        [
            WidgetView(friendlyName: "News", view: MusicView(), order: 1, selected: false),
            WidgetView(friendlyName: "Music", view: MusicView(), order: 2, selected: false),
            WidgetView(friendlyName: "Ambiance", view: AmbianceView(), order: 3, selected: true),
            WidgetView(friendlyName: "Weather", view: WeatherView(), order: 4, selected: false),
            WidgetView(friendlyName: "Routines", view: MusicView(), order: 5, selected: false)
        ]
    }
}

这是我的两个 SwiftUI 视图,第一个只是小文本框,第二个是滚动视图,但无法正常工作。

struct WidgetName: View {
    let widget: WidgetView
    
    var body: some View {
        ZStack(alignment: .center) {
            Color.clear
            Button(action: {
                print("Hello")
            }){
                ZStack{
                    Text(widget.friendlyName)
                        .fontWeight(.semibold)
                        .opacity(0)
                        .padding(widget.selected ? 15: 12)
                        .overlay(
                            RoundedRectangle(cornerRadius: 15)
                                    .fill(Color.white)
                                .opacity(widget.selected ? 1: 0.5)
                        )
                    Text(widget.friendlyName)
                        .fontWeight(.semibold)
                        .foregroundColor(.black)
                        .opacity(widget.selected ? 1: 0.5)
                }
            }
        }.padding()
    }
}

struct WidgetName_Previews: PreviewProvider {
    static var previews: some View {
        WidgetName(widget: WidgetView.data[1])
            .previewLayout(.fixed(width: 400, height: 60))
            .background(Color.purple)
        WidgetName(widget: WidgetView.data[2])
            .previewLayout(.fixed(width: 400, height: 60))
            .background(Color.purple)
        WidgetName(widget: WidgetView.data[3])
            .previewLayout(.fixed(width: 400, height: 60))
            .background(Color.purple)
    }
    
}
    struct WidgetNameStrip: View {
        var widgets: [WidgetView]
    
        var body: some View {
            VStack{
                Spacer()
                ScrollView(.horizontal, showsIndicators: false) {
                    
                    ScrollViewReader { value in
                        
                        Button("Jump to #32") {
                            value.scrollTo(3)
                        }
                        .foregroundColor(.white)
                        
                        HStack(spacing: 10) {
                            ForEach(widgets, id: \.self) { widget in
                                WidgetName(widget: widget)
                            }
                        }
                        
                    }
                    
                }
                .frame(height: 80)
            }
        }
        
    }
    
    struct WidgetNameStrip_Previews: PreviewProvider {
        static var previews: some View {
            WidgetNameStrip(widgets: WidgetView.data)
                .background(Color.purple)
                .previewDevice("iPhone 8")
        }
    }



对于您的代码,正如我所见,它需要使用 order 作为视图的 id,因为 scrollTo 使用 id/s 来查找视图,即:

Button("Jump to #32") {
    value.scrollTo(3)
}
.foregroundColor(.white)

HStack(spacing: 10) {
    ForEach(widgets, id: \.self) { widget in
        WidgetName(widget: widget)
            .id(widget.order)     // << here !!
    }
}

根据@Asperi 的回复,您还可以在建立 id 后在 .scrollTo 上使用锚参数。

锚点控制滚动视图中所选项目在滚动视图中的位置。

Button("Jump to #32") {
  value.scrollTo(id, anchor: .center)
}

您可以使用 withAnimation

对其进行动画处理
withAnimation { value.scrollTo(3, anchor: .center) }