让 ScrollView 始终滚动到底部

Ros*_*ipe 5 scrollview swiftui

我有一个固定高度的 ScrollView,其中包含一个 Text(),它不断从我的 viewModel 中更新。\n如果文本太多而无法一次查看,即我需要滚动才能看到文本的结尾,我\xe2 \x80\x99d 喜欢它自动滚动,这样我总是能看到文本的结尾。

\n

那可能吗?

\n
  ScrollView {\n            Text(vm.text)\n               .frame(minWidth: 20, alignment: .leading)\n\n             }\n             .frame(height: 200)\n
Run Code Online (Sandbox Code Playgroud)\n

注意:这是我的问题的一个非常简化的版本。在我的应用程序中,有时文本没有更新,但确实需要可滚动。

\n

我尝试过scrollViewReader \xe2\x80\xa6 类似的东西:

\n
 ScrollView {\n                ScrollViewReader() { proxy in\n                    Text(vm.text)\n                        .frame(minWidth: 20, alignment: .leading)\n                    Text("").id(0)\n                }\n            }\n            .frame(height: 200)\n
Run Code Online (Sandbox Code Playgroud)\n

想要滚动到空文本,但我无法\xe2\x80\x99t 弄清楚如何触发

\n
withAnimation {\n                proxy.scrollTo(0)\n            }\n
Run Code Online (Sandbox Code Playgroud)\n

...我见过的所有示例都使用按钮,但我需要在文本更新时触发。

\n

Ros*_*ipe 0

我已经通过@ChrisR修改了代码,并进行了修改,至少显示了我希望的scrollTo: 工作方式。它在 2 个几乎相同的具有不同 id 的视图之间切换(其中一个有一点点填充)

import SwiftUI

struct ContentView: View {
@State private var vmtext = "\n\n\n\nTest Text"
@State private var number = 0
@State private var id = 0
// timer change of text for testing
let timer = Timer.publish(every: 0.5, on: .main, in: .common).autoconnect()

var body: some View {
    ScrollViewReader { proxy in
        ScrollView {
            id == 0 ?
            VStack {
                Text(vmtext)
                    .padding(.top, 0)
                    .font(.title2)
                    .id(0)
            }
            :
            VStack {
                Text(vmtext)
                    .padding(.top, 1)
                    .font(.title2)
                    .id(1)
            }
        }
        // react on change of text, scroll to end
        .onChange(of: vmtext) { newValue in
            print("onChange entered. id: \(id)")
            proxy.scrollTo(id, anchor: .bottom)
        }
    }
    .frame(height: 90, alignment: .center)
    .frame(maxWidth: .infinity)
    .border(.primary)
    .padding()
    // timer change of text for testing
    .onReceive(timer) { _ in
        if id == 0 { id = 1} else {id = 0}
        number += 1
        vmtext += " word\(number)"
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

注意:似乎如果显示的文本视图的高度没有改变, proxy.scrollTo 就会被忽略。将填充设置为相同,黑客就会中断。

从 var vmtext 中删除“\n\n\n\n”可以打破黑客攻击。它们使文本视图的初始大小大于滚动视图窗口,因此可以立即滚动 - 或者其他:-)。如果您确实删除了它们,则在用手指进行初始滚动后,滚动将开始工作。

编辑:

这是一个没有填充和“\n\n\n\n” hack 的版本,它使用双旋转 hack

import SwiftUI

struct ContentView: View {
@State private var vmtext = "Test Text"
@State private var number = 0
@State private var id = 0
// timer change of text for testing
let timer = Timer.publish(every: 0.3, on: .main, in: .common).autoconnect()

var body: some View {
    ScrollViewReader { proxy in
        ScrollView {
            Group {
                id == 0 ?
                VStack {
                    Text(vmtext)
                        .font(.title2)
                        .id(0)
                }
                :
                VStack {
                    Text(vmtext)
                        .font(.title2)
                        .id(1)
                }
            }
            .padding()
            .rotationEffect(Angle(degrees: 180))
        }
        .rotationEffect(Angle(degrees: 180))
        // react on change of text, scroll to end
        .onChange(of: vmtext) { newValue in
            withAnimation {
                if id == 0 { id = 1 } else { id = 0 }
                proxy.scrollTo(id, anchor: .bottom)
            }
        }
    }
    .frame(height: 180, alignment: .center)
    .frame(maxWidth: .infinity)
    .border(.primary)
    .padding()
    // timer change of text for testing
    .onReceive(timer) { _ in
        number += 1
        vmtext += " word\(number)"
    }
  }
 }
Run Code Online (Sandbox Code Playgroud)

如果文本从视图顶部开始并且仅在文本填满视图时才滚动,就像 swiftui TextEditor 一样,那就太好了......并且 withAnimation 不起作用。