如何让 SwiftUI 中的水平滚动视图在更新封闭文本时自动滚动到结束

How to get a horizontal ScrollView in SwiftUI to scroll automatically to end when the enclosed Text is updated

我正在编写一个 SwiftUI iOS 应用程序,我需要一个 Text 视图来在内容更新时自动滚动到其内容的末尾。更新发生在模型中。为了不让这个问题与我的应用程序的细节复杂化,我创建了一个简单的场景,其中我有两个文本字段和一个文本标签。在文本字段中输入的任何文本都会连接起来并显示在文本标签中。文本标签包含在水平 ScrollView 中,如果文本长于屏幕宽度,可以手动滚动。我想要实现的是每当标签更新时文本自动滚动到末尾。

这里是简单的模型代码:

class Model: ObservableObject {
    var firstString = "" {
        didSet { combinedString = "\(firstString). \(secondString)." }
    }
    
    var secondString = "" {
        didSet { combinedString = "\(firstString). \(secondString)." }
    }
    
    @Published var combinedString = ""
}

这是内容视图:

struct ContentView: View {
    @ObservedObject var model: Model
    
    var body: some View {
        VStack(alignment: .leading, spacing: 10) {
            TextField("First string: ", text: $model.firstString)
            TextField("Second string: ", text: $model.secondString)
            Spacer().frame(height: 20)
            Text("Combined string:")
            ScrollView(.horizontal) {
                Text(model.combinedString)
            }
        }
    }
}

根据我所做的研究,我发现无需手动滚动到文本末尾的唯一方法是向视图添加一个按钮,这会导致标签中的文本滚动到末尾。

上面的 ScrollView 嵌入了一个 ScrollViewReader,带有一个按钮来实现滚动操作。

ScrollViewReader { scrollView in
    VStack {
        ScrollView(.horizontal) {
            Text(model.combinedString)
                .id("combinedText")
        }
        Button("Scroll to end") {
            withAnimation {
                scrollView.scrollTo("combinedText", anchor: .trailing)
            }
        }
        .padding()
        .foregroundColor(.white)
        .background(Color.black)
    }
}

这可行,前提是要使用按钮来实现滚动操作。

我的问题是:是否可以在模型更新时触发上面的滚动动作,而不需要点击按钮。

任何帮助或指点将不胜感激。

谢谢。

ScrollViewReader 是您正在寻找的解决方案。您可能需要尝试一下这个值。此外,您还需要将 .id(0) 修饰符添加到您的文本视图中。

ScrollView {
    ScrollViewReader { reader in 
        Button("Go to first then anchor trailing.") {
                    value.scrollTo(0, anchor: .trailing)
                }
      // The rest of your code .......

我假设你想要这个:

ScrollViewReader { scrollView in
    VStack {
        ScrollView(.horizontal) {
            Text(model.combinedString)
                .id("combinedText")
        }
        .onChange(of: model.combinedString) {     // << here !!
            withAnimation {
                scrollView.scrollTo("combinedText", anchor: .trailing)
            }
        }
    }
}