简体   繁体   English

SwiftUI ForEach 未在滚动视图中正确更新

[英]SwiftUI ForEach not correctly updating in scrollview

I have a SwiftUI ScrollView with an HStack and a ForEach inside of it.我有一个 SwiftUI ScrollView,里面有一个 HStack 和一个 ForEach。 The ForEach is built off of a Published variable from an ObservableObject so that as items are added/removed/set it will automatically update in the view. ForEach 是基于 ObservableObject 的 Published 变量构建的,因此当添加/删除/设置项目时,它将在视图中自动更新。 However, I'm running into multiple problems:但是,我遇到了多个问题:

  1. If the array starts out empty and items are then added it will not show them.如果数组开始为空,然后添加项目,则不会显示它们。
  2. If the array has some items in it I can add one item and it will show that, but adding more will not.如果数组中有一些项目,我可以添加一个项目,它会显示,但添加更多不会。

If I just have an HStack with a ForEach neither of the above problems occur.如果我只有一个带有 ForEach 的 HStack,则上述问题都不会发生。 As soon as it's in a ScrollView I run into the problems.一旦它在 ScrollView 中,我就会遇到问题。

Below is code that can be pasted into the Xcode SwiftUI Playground to demonstrate the problem.下面是可以粘贴到 Xcode SwiftUI Playground 中以演示问题的代码。 At the bottom you can uncomment/comment different lines to see the two different problems.在底部,您可以取消注释/注释不同的行以查看两个不同的问题。

If you uncomment problem 1 and then click either of the buttons you'll see just the HStack updating, but not the HStack in the ScrollView even though you see init print statements for those items.如果您取消注释问题 1,然后单击任一按钮,您将只看到 HStack 更新,而不会看到 ScrollView 中的 HStack,即使您看到这些项目的 init 打印语句。

If you uncomment problem 2 and then click either of the buttons you should see that after a second click the the ScrollView updates, but if you keep on clicking it will not update - even though just the HStack will keep updating and init print statements are output for the ScrollView items.如果您取消注释问题 2,然后单击其中一个按钮,您应该会在第二次单击后看到 ScrollView 更新,但如果您继续单击它不会更新 - 即使只是 HStack 将继续更新并输出 init 打印语句对于 ScrollView 项目。

import SwiftUI
import PlaygroundSupport
import Combine

final class Testing: ObservableObject {
    @Published var items: [String] = []

    init() {}

    init(items: [String]) {
        self.items = items
    }
}

struct SVItem: View {
    var value: String

    init(value: String) {
        print("init SVItem: \(value)")
        self.value = value
    }

    var body: some View {
        Text(value)
    }
}

struct HSItem: View {
    var value: String

    init(value: String) {
        print("init HSItem: \(value)")
        self.value = value
    }

    var body: some View {
        Text(value)
    }
}

public struct PlaygroundRootView: View {
    @EnvironmentObject var testing: Testing

    public init() {}

    public var body: some View {
        VStack{
            Text("ScrollView")
            ScrollView(.horizontal) {
                HStack() {
                    ForEach(self.testing.items, id: \.self) { value in
                        SVItem(value: value)
                    }
                }
                .background(Color.red)
            }
            .frame(height: 50)
            .background(Color.blue)
            Spacer()
            Text("HStack")
            HStack {
                ForEach(self.testing.items, id: \.self) { value in
                    HSItem(value: value)
                }
            }
            .frame(height: 30)
            .background(Color.red)
            Spacer()
            Button(action: {
                print("APPEND button")
                self.testing.items.append("A")
            }, label: { Text("APPEND ITEM") })
            Spacer()
            Button(action: {
                print("SET button")
                self.testing.items = ["A", "B", "C"]
            }, label: { Text("SET ITEMS") })
            Spacer()
        }
    }
}

// Present the view controller in the Live View window
PlaygroundPage.current.liveView = UIHostingController(
    // problem 1
    rootView: PlaygroundRootView().environmentObject(Testing())

    // problem 2
    // rootView: PlaygroundRootView().environmentObject(Testing(items: ["1", "2", "3"]))
)

Is this a bug?这是一个错误吗? Am I missing something?我错过了什么吗? I'm new to iOS development..I did try wrapping the actual items setting/appending in the DispatchQueue.main.async, but that didn't do anything.我是 iOS 开发的新手。我确实尝试在 DispatchQueue.main.async 中包装实际项目设置/附加,但这没有做任何事情。

Also, maybe unrelated, but if you click the buttons enough the app seems to crash.此外,也许不相关,但如果您点击按钮足够多,应用程序似乎会崩溃。

It is known behaviour of ScrollView with observed empty containers - it needs something (initial content) to calculate initial size, so the following solves your code behaviour已知ScrollView行为与观察到的空容器 - 它需要一些东西(初始内容)来计算初始大小,因此以下解决了您的代码行为

@Published var items: [String] = [""]

In general, in such scenarios I prefer to store in array some "easy-detectable initial value", which is removed when first "real model value" appeared and added again, when last disappears.通常,在这种情况下,我更喜欢在数组中存储一些“易于检测的初始值”,当第一个“真实模型值”出现时将其删除,并在最后一个消失时再次添加。 Hope this would be helpful.希望这会有所帮助。

Just ran into the same issue.刚刚遇到了同样的问题。 Solved with empty array check & invisible HStack用空数组检查和不可见的 HStack 解决

ScrollView(showsIndicators: false) {
    ForEach(self.items, id: \.self) { _ in
        RowItem()
    }

    if (self.items.count == 0) {
        HStack{
            Spacer()
        }
    }
}

For better readability and also because the answer didn't work for me.为了更好的可读性,也因为答案对我不起作用。 I'd suggest @TheLegend27 answer to be slightly modified like this:我建议@TheLegend27 答案稍微修改如下:

if self.items.count != 0 {

   ScrollView(showsIndicators: false) {
       ForEach(self.items, id: \.self) { _ in
           RowItem()
       }
   }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM