当在GeometryReader中进行更改时,SwiftUI不会反映对@Binding @State变量的更改

问题描述 投票:0回答:1

我有一个ScrollView,带有HStack视图,这些视图会根据自己的位置进行动画处理。为此,我使用的是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

[可以请人解释这里发生了什么吗?这是预期的行为还是错误?

swift swiftui combine swiftui-bug geometryreader
1个回答
0
投票

好,出现问题是因为在状态更改时会评估整个树,因此应牢记在代码中更改状态,然后重新评估代码。您甚至会在XCode中收到警告,直接更改状态会导致不可预测的行为。

这本质上意味着通过另一个线程执行将完成这项工作,这就是DispatchQueue.main.asyncAfter路由起作用的原因,但是您仍然需要考虑对树进行重新评估。

© www.soinside.com 2019 - 2024. All rights reserved.