如何以编程方式在 SwiftUI 中滚动?

Posted

技术标签:

【中文标题】如何以编程方式在 SwiftUI 中滚动?【英文标题】:How can I programmatically scroll in 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) 【参考方案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 的设计行为。请阅读以下文本:***.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

【讨论】:

以上是关于如何以编程方式在 SwiftUI 中滚动?的主要内容,如果未能解决你的问题,请参考以下文章

在 iOS 13 上以编程方式滚动 SwiftUI ScrollView

如何在 SwiftUI 中将表单滚动到特定的 UI 元素

如何以编程方式从 SwiftUI 列表中删除行并刷新列表视图?

如何在 iOS 应用程序中以编程方式检测 SwiftUI 的使用情况

如何以编程方式创建等效的 SwiftUI 堆栈视图分隔符

如何在 SwiftUI 中禁用位置动画?