SwiftUI: 调用以编程方式创建的视图的函数

3
我尝试在一个通过程序调用的视图中,使 SwiftUI 的 ScrollView 在按下按钮时滚动到抽象视图中的某一点。以下是我的代码:
struct AbstractedView: View {
    @Namespace var view2ID

    var body: some View {
        ScrollView {
            VStack {
                View1()
                View2()
                .id(view2ID)
                View3()
            }
        }
    }
    func scrollToView2(_ proxy: ScrollViewProxy) {
        proxy.scrollTo(view2ID, anchor: .topTrailing)
    }
}

正如你所看到的,在ScrollViewReader中调用scrollToView2()时,AbstractedView将滚动到view2ID。 我在另一个视图中以编程方式创建了多个AbstractedView
struct HigherView: View {
    var numAbstractedViewsToMake: Int

    var body: some View {
        VStack {
            HStack {
                ForEach (0..<numAbstractedViewsToMake, id: \.self) { _ in
                    AbstractedView()
                }
            }
            Text("button")
            .onTapGesture {
                /* call each AbstractedView.scrollToView2()
            }
        }
    }
}

如果我将每个AbstractedView与一个ScrollViewReader一起存储在HigherView结构内的数组中,这样做可以吗?我感觉应该有更好的方法来实现这个,只是不知道该如何做。由于我对Swift还很新,所以非常感谢您的帮助。
P.S. 我听说过UIKit,但我对它一无所知,现在适合使用它吗?

2
试图在不同的 View 上“调用函数”有些违反 SwiftUI 的原则。如果您需要 AbstractedView 响应某些状态更改,请将状态存储在父视图 (HigherView) 中,并通过 props 向下传递。 - jnpdx
我起初尝试了这个,但是我没找到一种方法让ScrollView在状态变化时调用scrollToView2() - Randy
我知道 .OnChange(of:) 修饰符,但是我无法弄清楚在这种情况下如何使用它。 - Randy
3
这样行不通,scrollTo需要一个ID来滚动到指定位置(每个事件)。例如,请参阅此处https://dev59.com/uFIH5IYBdhLWcg3wKKBU#60855853。 - Asperi
1个回答

0

借鉴@Asperi和@jnpdx的评论,我得出了一个比我需要的更强大的解决方案:

class ScrollToModel: ObservableObject {
    enum Action {
        case end
        case top
    }
    @Published var direction: Action? = nil
}

struct HigherView: View {
    @StateObject var vm = ScrollToModel()
    var numAbstractedViewsToMake: Int

    var body: some View {
        VStack {
            HStack {
                Button(action: { vm.direction = .top }) { // < here
                    Image(systemName: "arrow.up.to.line")
                      .padding(.horizontal)
                }
                Button(action: { vm.direction = .end }) { // << here
                    Image(systemName: "arrow.down.to.line")
                      .padding(.horizontal)
                }
            }
            Divider()
            HStack {
                ForEach(0..<numAbstractedViewsToMake, id: \.self) { _ in
                    ScrollToModelView(vm: vm)
                }
            }
        }
    }
}

struct AbstractedView: View {
    @ObservedObject var vm: ScrollToModel

    let items = (0..<200).map { $0 } // this is his demo
    var body: some View {
        VStack {
            
            ScrollViewReader { sp in
                ScrollView {
               
                    LazyVStack { // this bit can be changed accordingly
                        ForEach(items, id: \.self) { item in
                            VStack(alignment: .leading) {
                                Text("Item \(item)").id(item)
                                Divider()
                            }.frame(maxWidth: .infinity).padding(.horizontal)
                        }
                    }.onReceive(vm.$direction) { action in
                        guard !items.isEmpty else { return }
                        withAnimation {
                            switch action {
                                case .top:
                                    sp.scrollTo(items.first!, anchor: .top)
                                case .end:
                                    sp.scrollTo(items.last!, anchor: .bottom)
                                default:
                                    return
                            }
                        }
                    }
                }
            }
        }
    }
}

非常感谢你们俩!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接