SwiftUI在ForEach中设置对象变量不起作用

时间:2019-11-22 04:14:41

标签: swiftui

我有一个滚动视图,它在ForEach中以文本视图的形式显示对象的名称,我还有一个GeometryReader,因为我需要知道每个对象在ScrollView中的位置。显示文本视图时,我有一个函数 showObject(),根据GeometryReader将对象的编号设置为它的位置。

这是我的问题。在ScrollView上方,我有3个文本,显示用于调试目的的对象的名称和数量。滚动时,对象[0]的数字会像我期望的那样更新,但是对象[1]和对象[2]保持在初始化的100值。我在 showObject()函数中设置了打印图像,它正在接收正确的信息,但是似乎无法在object [0]之后的对象上设置编号。有人知道为什么会这样吗?还是实现我想要做的更好的方法?

struct MyObject {
    var name:String
    var number:CGFloat = 100
}


struct MyScrollView: View {

    @State var objects: [MyObject] = [MyObject(name: "a"), MyObject(name: "b"), MyObject(name: "c")]

    var body: some View {
        VStack{

            Text(objects[0].name + " --- " + (objects[0].number.description))
            Text(objects[1].name + " --- " + (objects[1].number.description))
            Text(objects[2].name + " --- " + (objects[2].number.description))

                ScrollView(showsIndicators: false){

                    VStack{

                        ForEach(self.options.indices) { i in

                            GeometryReader {geo in

                                self.showObject(index: i, num: geo.frame(in: .global).minY)

                            } // geo

                        } // ForEach

                    } // VStack inside ScrollView

                } // ScrollView

        } // top vstack

    } // body


    func showObject(index: Int, num: CGFloat) -> Text
    {
        objects[index].number = num


        print (objects[index].name + " -- should be: " + num.description + " -- actual: " + objects[index].number.description)
        return Text(objects[index].name)
    }

} // view

1 个答案:

答案 0 :(得分:0)

@State var objects中的任何更改都会导致您的MyScrollView重绘,即重新娱乐。因此,渲染引擎开始绘制MyScrollView,来到objects[index].number = num,它修改了@State var objects,该报告显示需要进行新的重绘...

a -- should be: 94.0 -- actual: 100.0
2019-11-22 08:14:11.535085+0200 Testing[73866:816126] [SwiftUI] Modifying state during view update, this will cause undefined behavior.

...渲染中断并从头开始。这就是为什么您只观察到第一个MyObject变化。

解决方案...那么,您需要将.number移出影响递归重绘的工作流。最简单的是将修改推迟到下一个事件周期,例如

if self.objects[index].number != num { // avoid redraw for same values
    DispatchQueue.main.async {
        self.objects[index].number = num
    }
}

但是,通常最好将MyModel放在视图之外,并进行一些ViewModel包装,该包装应具有效果发布的name而不是number,但这取决于您的真正需求。