当从 GeometryReader 中进行更改时,SwiftUI 不会反映对 @Binding @State 变量的更改
SwiftUI would not reflect changes to a @Binding @State variable when the change is made from within a GeometryReader
我有一个带有 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()
}
}
当您点击 "toggle" 按钮时,该按钮也从 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
有人可以解释一下这是怎么回事吗?这是预期行为还是错误?
好的,出现问题是因为在状态更改时将评估整棵树,因此在编写将重新评估的代码中更改状态时应牢记这一点。您甚至会在 XCode 中收到警告,直接更改状态可能会导致不可预测的行为。
这基本上意味着通过另一个线程执行即可完成这项工作,这就是 DispatchQueue.main.asyncAfter 路由有效的原因,但您仍然需要考虑到树将被重新评估。
我有一个带有 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()
}
}
当您点击 "toggle" 按钮时,该按钮也从 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
有人可以解释一下这是怎么回事吗?这是预期行为还是错误?
好的,出现问题是因为在状态更改时将评估整棵树,因此在编写将重新评估的代码中更改状态时应牢记这一点。您甚至会在 XCode 中收到警告,直接更改状态可能会导致不可预测的行为。
这基本上意味着通过另一个线程执行即可完成这项工作,这就是 DispatchQueue.main.asyncAfter 路由有效的原因,但您仍然需要考虑到树将被重新评估。