ScrollView内的LazyVStack中高度可变的内容会导致断断续续/跳跃



XCode版本13.0测试版(13A5155e(&瞄准iOS 14或15

我的目标是在SwiftUI中创建一个聊天视图。这需要创建一个具有不同高度内容的ScrollView。

经过广泛的调试,我确定,如果ScrollView中的视图没有固定的高度,当你滚动到视图顶部时,它会断断续续。

––––

项目:下载此项目并自己尝试

struct Message: Identifiable {
let id = UUID()
var text: String
}
struct ContentView: View {
@State var items: [Message] = MockData.randomMessages(count: 100)

var body: some View {
VStack {
Button("Shuffle items") {
items = MockData.randomMessages(count: 100)
}
ScrollView {
LazyVStack(spacing: 10) {
ForEach(items) { item in
Text(item.text)
.background(colors.randomElement()!)
}
}
}
}
}
}

我现在的结论是LazyVStack只适用于具有固定高度的子视图。仅此问题就阻止了SwiftUI做好生产准备。

有其他人解决过这个问题吗?

苹果回应(2021年7月27日(:

"在你的Mac目标上,这一切都有效,但我看到iOS上存在滚动问题。这个问题肯定是iOS上SwiftUI的一个错误。我建议您不要重写您的应用程序,而是使用UIViewRepresentable作为UIScrollView(或者实际上UITable/UICollection View在这里最有意义(。如果您使用可重复使用的视图,如表或集合,这些问题几乎肯定会消失。你不需要重写你的应用程序,但如果这个问题阻止了发布,你应该添加UIViewRepresentable">

在iOS 15中,此错误似乎已修复。

在iOS 14中,我建议在VStack中显示前n个项目(足以用缓冲区填充屏幕高度(,在LazyVStack显示其余项目。我发现在大多数情况下,这会消除抖动。

ScrollView {
VStack(spacing: 10) {
ForEach(items.prefix(10)) { item in
Text(item.text)
.background(colors.randomElement()!)
}
}
LazyVStack(spacing: 10) {
ForEach(items.dropFirst(10)) { item in
Text(item.text)
.background(colors.randomElement()!)
}
}
}
}
}           

对我来说,抖动是由ForEach中的视图附加了onAppear修饰符引起的(因此它在集合中的每个项上都运行(。即使钩子在后台线程中运行,即使它什么都没做,也会出现抖动。

虽然没有100%修复,但我的解决方案是只在真正需要的元素中添加onAppear修饰符。在我的情况下,我使用onAppear进行连续滚动(分页(。通过只将onAppear添加到列表中的第n个项目,可以在接近列表末尾时将抖动大大减少到一个小的光点。

下面是一篇关于如何有条件地应用修饰符的好文章。

不确定这是否会帮助那些还不能将最低版本升级到iOS 15的人,但希望能帮助他们!

在macos 12测试版、xcode 13测试版、target ios 15和macCatalyst上运行时没有任何问题。在ios15设备和macos12上进行了测试。我也试过用10000,效果很好。也许你的问题发生在旧的ios和macos上。你可能对Swift UI感兴趣,它被频繁的@StateObject更新淹没了?其中代码在ios14上很吃力,但在ios15上却不吃力。

您可以尝试其他方法来查看是否可以提高性能,例如:

ForEach(items.indices, id: .self) { index in
Text(items[index]).background(colors.randomElement()!)
}

ForEach(Array(items.enumerated()), id: .0) { index, item in
Text(item).background(colors.randomElement()!)
}

最新更新