【问题标题】:How can I programmatically scroll in SwiftUI?如何以编程方式在 SwiftUI 中滚动?
【发布时间】:2021-03-30 15:33:48
【问题描述】:

在 SwiftUI 中有一种滚动到视图的方法。像这个例子:

ScrollView {

    ScrollViewReader { value in

        Button("Jump to #8") {

            // we scroll on button press
            value.scrollTo(8, anchor: .top)
        }
        .padding()

        ForEach(0..<100) { i in

            Text("Example \(i)")
                .font(.title)
                .frame(width: 200, height: 200)
                .background(colors[i % colors.count])
                
                // We need to set ID for scrolling
                .id(i)

        }
    }
}

但我的用户界面并不是那么简单。我有一个视图和视图模型。也有一些延迟生成的视图。

在加载所有视图后,我需要滚动到某个元素。

Do it after 不是问题,但是如何从 ViewModel 以编程方式滚动到需要的元素?


@ObservedObject var model: someViewModel

var body: some View {
    ScrollView {
        ScrollViewReader { pageScroller in

            ForEach(0..<100) { i in
                Text("Example \(i)")
                    .frame(width: 200, height: 200)
                    .background(colors[i % colors.count])
                    .id(i)
            }
        }
    }
    .frame(height: 350)
}

class someViewModel: ObservableObject {
    @Published var scrollToPageName = 4
    var needToScroll = false

    init() { }
}

如何在不刷新页面的情况下从我的model 执行pageScroller.scrollTo(pageIndex, anchor: .top)

或者如何通过一些事件从模型中执行一些视图函数?

我也不需要在滚动(!)之前刷新页面 - 就像第一个示例中的按钮单击一样。

【问题讨论】:

  • 您想知道如何滚动和使用pageScroller吗?
  • @swiftPunk 我需要知道如何从我的viewModel 拨打pageScroller.scrollTo(pageIndex, anchor: .top)

标签: swift swiftui


【解决方案1】:

滚动不是视图模型的责任,但您可以在视图模型中添加selectedIndex 属性,然后观察并响应更改。

var body: some View {
    ScrollView {
        ScrollViewReader { pageScroller in
            ForEach(0..<100) { i in
                Text("Example \(i)")
                    .frame(width: 200, height: 200)
                    .background(colors[i % colors.count])
                    .id(i)
            }
            .onChange(of: viewModel.selectedIndex) { newIndex in
                pageScroller.scrollTo(newIndex, anchor: .top)
            }
        }
    }
    .frame(height: 350)
}

由于您提到您的内容是异步加载的,因此您可以在加载所有内容后更新selectedIndex 属性。

【讨论】:

  • 太棒了!非常感谢!
  • is viewModel.selectedIndex 必须是 @Published 才能获得这样的代码?
  • 一定是!当然。
  • @swiftPunk 这不是问题。这是 SwiftUI 的设计行为。请阅读以下文本:stackoverflow.com/a/62919526/4423545 了解在 swiftUI 中使用 vievModels 的基础知识
  • @swiftPunk 我已经解决了问题并为我的所有问题找到了解决方案,你们都帮助了我,swiftPunk 和 EmilioPelaez。我将在几个小时内编写替代答案,无需刷新视图即可使用。
【解决方案2】:

重要! 仅当不刷新视图很重要时,此答案才会对您有用。在另一种情况下,最好使用 EmilioPelaez

的解决方案

这个答案展示了如何:

  • 从模型发送事件到视图

  • 并在事件触发时执行一些函数

  • 不会触发全视图刷新!只有代码会像按下按钮一样被执行!

@ObservedObject var model: someViewModel

var body: some View {
    ScrollView {
        ScrollViewReader { pageScroller in

            ForEach(0..<100) { i in
                Text("Example \(i)")
                    .frame(width: 200, height: 200)
                    .background(colors[i % colors.count])
                    .id(i)
            }
            //Magic here
            .onReceive(model.scroller.objectWillChange) {
                pageScroller.scrollTo(model.scroller.scrollToPageName, anchor: .top)
            }
        }
    }
    .frame(height: 350)
}

class someViewModel: ObservableObject {
    //Magic here
    @ObservedObject var scroller: scrollerHelper

    init() {
        someDelayedCall()
    }

    func someDelayedCall(){
         scroller.scrollToPageName = 8
    }
}

//Magic here
class scrollerHelper: ObservableObject {
    @Published var scrollToPageName: Int = -1
}

【讨论】:

    猜你喜欢
    • 2020-07-06
    • 2020-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-14
    • 2011-07-04
    • 2017-04-13
    相关资源
    最近更新 更多