当从 GeometryReader 中进行更改时,SwiftUI 不会反映对 @Binding @State 变量的更改
Posted
技术标签:
【中文标题】当从 GeometryReader 中进行更改时,SwiftUI 不会反映对 @Binding @State 变量的更改【英文标题】:SwiftUI would not reflect changes to a @Binding @State variable when the change is made from within a GeometryReader 【发布时间】:2020-04-22 07:15:44 【问题描述】:我有一个带有 HStack 视图的 ScrollView,这些视图会根据它们自己的位置进行动画处理。为此,我使用了一个 GeometryReader,它将读取每个视图的当前全局位置,当到达某个位置时,视图将从失焦状态切换到聚焦状态,并进行一些转换并将 @State 变量更改为表明视图处于焦点状态,以便视图的其他部分可以对其进行操作。
相当简单,对吧?不幸的是,当我从 GeometryReader 中更改 @State 时,不会发生状态更改。因此,我构建了一个简化版本来演示该问题。
代码如下:
import SwiftUI
struct GeometryReaderTest: View
var body: some View
ScrollView(.horizontal, showsIndicators: false)
//
HStack(spacing: 40)
ForEach(0..<10) _ in
SlideView()
struct SlideView : View
//
@State var isInFocus : Bool = false
var body: some View
ZStack
Rectangle().fill( isInFocus ? Color.green : Color.orange).frame(width: UIScreen.main.bounds.width - 60, height: 120, alignment: .center)
MyContent(isInFocus: self.$isInFocus)
struct MyContent : View
@Binding var isInFocus : Bool
var body: some View
GeometryReader geo in
self.toggleFocusState(geometry: geo)
func toggleFocusState(geometry : GeometryProxy)-> some View
let distance = geometry.frame(in: .global).origin.x.magnitude
self.isInFocus = distance < 120
return VStack
Text("Focused: \(self.isInFocus ? "Yes" : "Nope" )")
Text("Distance: \(distance)")
Button(action:
//
self.isInFocus.toggle()
)
Text("Toggle")
struct GeometryReaderTest_Previews: PreviewProvider
static var previews: some View
GeometryReaderTest()
当您点击同样从 GeometryReader 中运行的“切换”按钮时,@State 变量将成功更改并反映更改。
当您实际滚动 ScrollView 并让它自然滚动时,什么都不会发生。即使停止,它也不会更新。
请看这里:https://streamable.com/d3op74
当您在 ScrollView 中缓慢滚动而不抬起手指时,@State 变量会发生变化。
这里:https://streamable.com/7m15t9
如果您像这样使用 DispatchQueue.main.asyncAfter() 引入延迟:
DispatchQueue.main.asyncAfter(deadline: .now() + 0.0)
self.isInFocus = distance < 120
即使延迟为 0,@State 更改也会在您滚动时起作用,无论多快,但是这次切换按钮不起作用(这是有道理的,因为 GeometryReader 会重新计算并推翻按钮执行的操作)。
这里:https://streamable.com/q3ylu1
请有人解释一下这里发生了什么?这是预期的行为还是错误?
【问题讨论】:
有意。您无法在渲染期间更改状态变量,因为它会循环渲染,因此渲染器只会忽略此类尝试。当您通过任何异步调用延迟状态更改时,您自己会中断循环,因此它可以工作 - 实际上这是通常的做法。 谢谢。有这方面的阅读材料吗?另外,处理这种情况的正确方法是什么? 【参考方案1】:好的,问题出现了,因为在状态更改时会评估整个树,因此在编写要重新评估的代码中更改状态时应该牢记这一点。您甚至会在 XCode 中收到警告,直接更改状态会导致不可预知的行为。
这实质上意味着通过另一个线程执行将完成这项工作,这就是 DispatchQueue.main.asyncAfter 路由有效的原因,但您仍然需要考虑到树将被重新评估。
【讨论】:
以上是关于当从 GeometryReader 中进行更改时,SwiftUI 不会反映对 @Binding @State 变量的更改的主要内容,如果未能解决你的问题,请参考以下文章
当从执行路径更改的表单中触发取消按钮时,组件更改会暂时暂停?
当从不同的类调用更改时,UIView IBOutlet 不会更改
当从另一个 ObservedObject 链接到 Published 属性时,视图不会对更改做出反应