SwiftUI ScrollView动画未按预期工作

gab6jxml  于 2023-09-30  发布在  Swift
关注(0)|答案(1)|浏览(131)

我在SwiftUI中遇到了一个问题,动画似乎无法在ScrollView中按预期工作。我已经创建了一个最小的、可重复的示例,其中包含一个消息列表,当添加新消息时,我希望滚动到列表的底部,并显示平滑的动画。然而,动画并不一致或平滑地发生。什么可能导致这个问题,我如何解决它?
我有一个SwiftUI视图,其中包含一个ScrollView,其中包含在类似聊天界面中显示的消息列表。当新邮件添加到列表中时,我希望视图自动滚动到底部以显示最新邮件。为了实现这一点,我使用了onChange修饰符沿着proxy.scrollTo方法, Package 在一个withAnimation块中。但是,动画并不始终按预期工作。

import SwiftUI

 struct ContentView: View {
@State private var chats = ["Message 1", "Message 2", "Message 3"]

var body: some View {
    VStack {
        ScrollViewReader { proxy in
            ScrollView {
                ForEach(chats, id: \.self) { message in
                    Text(message)
                        .padding()
                        .id(message)
                        .onChange(of: chats.count) { newCount in
                            if newCount > 0 {
                                withAnimation(.easeInOut(duration: 1.0)) {
                                    proxy.scrollTo(chats.indices.last, anchor: .bottom)
                                }
                            }
                        }
                }
            }
        }
    }
}
  }
zpf6vheq

zpf6vheq1#

我无法在运行iPadOS 16.6.1(目前最新发布的版本)的iPad上重现您的问题。
也就是说,我在您的代码中看到了一个真实的的bug和一个可能也会给您带来问题的可疑内容。
真实的的bug是使用.id(message)标记每个Text,其中messageString。但是你说proxy.scrollTo(chats.indices.last, ...),其中chats.indices.lastInt。所以你的滚动目标永远不会被代理找到。
有问题的是,您将onChange修饰符附加到每个Text。如果有100条消息,您需要所有100条消息来运行onChange闭包吗?答:没有。尝试将onChange修改器附加到ScrollView中的单个视图。我会将所有的Text封装在一个VStack中,并将onChange连接到VStack,如下所示:

import PlaygroundSupport
import SwiftUI

struct ContentView: View {
    @State private var chats = (1 ... 15).map { "Message \($0)" }
    
    var body: some View {
        VStack {
            Button("Add new message") {
                chats.append("Message \(chats.count + 1)")
            }
            ScrollViewReader { proxy in
                ScrollView {
                    VStack {
                        ForEach(chats, id: \.self) { message in
                            Text(message)
                                .padding()
                                .id(message)
                        }
                    }
                    .onChange(of: chats.count) { newCount in
                        if newCount > 0 {
                            withAnimation(.easeInOut(duration: 1.0)) {
                                proxy.scrollTo(chats.last!, anchor: .bottom)
                            }
                        }
                    }
                }
            }
        }
    }
}

PlaygroundPage.current.setLiveView(ContentView())

相关问题