onPreferenceChange 在 GeometryReader 框架更改时未被调用

huangapple go评论69阅读模式
英文:

onPreferenceChange not called on geometryReader frame change

问题

`PreferenceKey` 定义

```swift
struct ScrollPrefKey: PreferenceKey {
  static var defaultValue: CGFloat = 0

  static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
    value = nextValue()
  }
}

主要代码

ScrollView {
     GeometryReader { proxy in
         Text("\(proxy.frame(in: .named("scroll")).minY)") // 我可以看到变化
         Color.clear.preference(key: ScrollPrefKey.self, value: proxy.frame(in: .named("scroll")).minY)
     } //MARK: 结束 GeometryReader

     VStack {
       // 主要内容
       Button(action: {}, label: {Text("我的按钮")})
     }

 }
 .coordinateSpace(name: "scroll")
 .onPreferenceChange(ScrollPrefKey.self, perform: { value in
    print(value)
    // 做一些操作 - 但它从未触发
 })
  1. 这段代码在预览中工作,但在模拟器中不工作(iOS 16.1)。
  2. 将 VStack 替换为具有定义高度的静态 Color 可以工作。
  3. 嵌入包括 GeometryReader 在内的整个 scrollView 内容仍然不起作用。

<details>
<summary>英文:</summary>

`PreferenceKey` definition

    struct ScrollPrefKey: PreferenceKey {
      static var defaultValue: CGFloat = 0
    
      static func reduce(value: inout CGFloat, nextValue: () -&gt; CGFloat) {
        value = nextValue()
        
      }
    }

Main Code

     ScrollView {
         GeometryReader { proxy in
                        
             Text(&quot;\(proxy.frame(in: .named(&quot;scroll&quot;)).minY)&quot;) // I can see changes
                            
             Color.clear.preference(key: ScrollPrefKey.self, value: proxy.frame(in: .named(&quot;scroll&quot;)).minY)
         } //MARK: END GeometryReader

         VStack {
           //main content
           Button(action: {}, label: {Text(&quot;myButton&quot;)})
         }

     }
     .coordinateSpace(name: &quot;scroll&quot;)
     .onPreferenceChange(ScrollPrefKey.self, perform: { value in
        print(value)
        //DO SOME THINGS - but it never trigger
     })

1. This code works in Preview but not using simulator (iOS 16.1)
2. Replacing VStack with a static Color with defined height works
3. Embedding the entire content of scrollView including `GeometryReader` still doesn&#39;t work

</details>


# 答案1
**得分**: 2

`nextValue: () -> Self.Value`:这个函数允许对所有这些偏好进行逻辑归约,如果多个不同的视图输出多个值,都使用相同的PreferenceKey。

当视图首次加载时,偏好键将从`defaultValue`更新到初始值。由于只有一个视图产生带有一个偏好键的值,所以在这种情况下`nextValue()`返回nil。

`value: inout Self.Value`:
尽管`value`在之前的调用中持续累积并随着偏好键值的更改而更新。因此,我们应该在这里使用`value`。

在代码中使用以下方式更新`PreferenceKey`的定义:

```swift
struct ScrollPrefKey: PreferenceKey {
    static var defaultValue: CGFloat? = nil
    
    static func reduce(value: inout CGFloat?, nextValue: () -> CGFloat?) {
        value = value ?? nextValue()
    }
}
英文:
static func reduce(value: inout Self.Value, nextValue: () -&gt; Self.Value)

nextValue: () -&gt; Self.Value : This function allows for logic to reduce all those preferences to a single value, if multiple values are outputted by multiple different views, all using the same PreferenceKey.

When the view loads first time, the preference key will be updated from defaultValue to initial value. And since we have only one view producing the value with one preference key, the nextValue() returns nil in this case.

value: inout Self.Value:
Though the value, which is keep accumulating through previous calls and keeps updating with the preference key value changes. So we should use the value here.

Update the PreferenceKey definition in the code with this:

struct ScrollPrefKey: PreferenceKey {
    static var defaultValue: CGFloat? = nil
    
    static func reduce(value: inout CGFloat?, nextValue: () -&gt; CGFloat?) {
        value = value ?? nextValue()
    }
}

huangapple
  • 本文由 发表于 2023年1月9日 18:47:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/75056173.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定