SwiftUI:PreferenceKey Reduce 方法永远不会被调用

Posted

技术标签:

【中文标题】SwiftUI:PreferenceKey Reduce 方法永远不会被调用【英文标题】:SwiftUI: PreferenceKey Reduce method never gets called 【发布时间】:2020-03-20 22:30:54 【问题描述】:

我正在尝试使用 Preferences 将数据向上传递到视图链,但从未调用过 .onPreferenceChange。调查了一下发现,虽然调试器显示执行通过我的 .Preference 修饰符,reduce 方法永远不会被调用来将新值推送到键中。我不知道为什么,也找不到检查 View 的 Preference key:value 数据的方法。

代码如下:

struct LittleTapPrefKey: PreferenceKey 
    typealias Value = String
    static var defaultValue: Value = "A0"
    static func reduce(value: inout Value, nextValue: () -> String) 
        print("Inside Little Reduce \(value)and NextVal =  \(nextValue())")
        value = nextValue()
    

这里是单元格视图,点击 QGrid 中的 GlyphCell 实例,设置 Pref 修饰符:

struct GlyphCell: View 

    var glyph:Glyph
    @State private var selected = false   // << track own selection

    var body: some View 
        ZStack 
            Text("\(glyph.Hieroglyph)")
                .lineLimit(1)
                .padding(.all, 12.0)
                .font(.system(size: 40.0))
                .background(Color.yellow)
            Text("\(glyph.Gardiner)")
                .offset(x: 12, y: 28)
                .foregroundColor(.blue)
        .onTapGesture 
            print("Tap on \(self.glyph.Gardiner)")
            self.selected.toggle()
        
        .cornerRadius(6.0)
        .frame(width: 90.0, height: 100.0, alignment: .center)
        .previewLayout(.sizeThatFits)
        .background(Group 
            if self.selected 
                Color.clear
                    .preference(key: LittleTapPrefKey.self, value: self.glyph.Gardiner)
             // end selected test
           // end  group
      )     // end .background
           // end cell body view

在 .pref(key:...) 上设置断点后,每次点击单元格时都会触发断点,此时 self.glyph.Gardiner 的值是正确的。

Preference Key Struct "reduce" 函数中的断点或 test Print 语句永远不会被触发。由于 .onPrefChange 永远不会被触发,我必须假设 Value 永远不会改变,但我无法直接查看 Pref 值。

发生了什么事?

【问题讨论】:

【参考方案1】:

reduce 在您在一个视图层次结构中多次设置一个偏好键时被调用,因此 SwiftUI 为您提供了以某种方式组合它们的可能性,例如:

.previewLayout(.sizeThatFits)
.preference(key: LittleTapPrefKey.self, value: value1) // 1st time
.background(Group 
    if self.selected 
        Color.clear
            .preference(key: LittleTapPrefKey.self, value: value2) // 2nd time
    

在您的情况下,您只有一个值设置器,所以它只是设置并且.onPreferenceChange(LittleTapPrefKey.self) 回调修饰符按预期工作。

使用 Xcode 11.4 / ios 13.4 测试

【讨论】:

我知道reduce的功能,但不知道它在触发更新存储的关键位置方面有多聪明。在我的例子中,self.selected 块中的 set key 语句似乎正在执行,并且字形是正确的,但没有回调。您建议使用(垃圾)值对首选项键进行“第一次调用”,然后将键设置为所需的值。因此,它会在调用单元格视图时两次更改设置。我不明白为什么它需要两个设置,但你的其他建议是正确的,所以我今天早上会试试!再次感谢 Asperi!!!! @BlueskyMed,这不是一个建议,而是 reduce 可以工作的案例演示。当然你不应该设置 pref 两次。 啊!尽管如此,在 QGrid 的第一轮设置之后,父单元中的回调永远不会在选择单元格之后被调用。这是一个奇怪的异常现象!我在 QGrid 父级的父级视图中添加了一个 onPrefChange,它也不会触发。有趣的是,现在触发了 reduce,打印语句中的 NextVal 始终是添加的 setPref 中的“垃圾”值,而不是 isSelected 块内的值! 我在reduce里面加了一个调试器PO stmt,看到每次点击,reduce被调用了20多次!首先使用正确的值,然后使用默认值!回到绘图板!

以上是关于SwiftUI:PreferenceKey Reduce 方法永远不会被调用的主要内容,如果未能解决你的问题,请参考以下文章

在 SwiftUI 中理解通信模式“Preference Key”的问题

在 SwiftUI 中理解通信模式“Preference Key”的问题

SwiftUI:使用matchedGeometryEffect控制视图上的zIndex

SwiftUI |警告:绑定首选项 _ 尝试每帧更新多次。可能的原因?

SwiftUI ScrollView:查找当前在屏幕中心可见的子视图。子视图在 ScrollView 中的位置。偏好键。滚动视图代理

滚动视图下的 SwiftUI 点击