我有一个滚动视图,它在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
答案 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
,但这取决于您的真正需求。