SwiftUI - AppStorage 不适用于 GeometryReader

Posted

技术标签:

【中文标题】SwiftUI - AppStorage 不适用于 GeometryReader【英文标题】:SwiftUI - AppStorage doesn't work with GeometryReader 【发布时间】:2021-09-06 11:30:58 【问题描述】:

这是一个简单的例子。您可以创建新的 SwiftUI ios 项目并将其复制到 ContentView 文件中。

import SwiftUI

struct Settings 
    static let onOff = "onOff"


struct ContentView: View 
    @AppStorage(wrappedValue: false, Settings.onOff) var onOff
    
    var body: some View 
        NavigationView 
            GeometryReader  reader in // < Comment out this line
                List 
                    Section (header:
                                VStack 
                                    HStack 
                                        Spacer()
                                        VStack 
                                            Text("THIS SHOULD BE FULL-WIDTH")
                                            Text("It is thanks to GeometryReader")
                                        
                                        Spacer()
                                    
                                    .padding()
                                    .background(Color.yellow)
                                    
                                    HStack 
                                        Text("This should update from AppStorage: ")
                                        Spacer()
                                        Text(onOff == true ? "ON" : "OFF")
                                    
                                    .padding()
                                
                                .frame(width: reader.size.width) // < Comment out this line
                                .textCase(nil)
                                .font(.body)
                    ) 
                        Toggle(isOn: $onOff) 
                            Text("ON / OFF")
                        
                    
                    
                
                .listStyle(GroupedListStyle())
             // < Comment out this line
            
        
    


struct ContentView_Previews: PreviewProvider 
    static var previews: some View 
        ContentView()
    

我有 3 个元素:

    带有黄色背景的文本 - 我需要它是全角的,我使用 GeometryReader 来完成。 文本。最后一句话应该根据切换值打开/关闭。这仅用于测试目的,以检查 AppStorage 是否正常工作。 Toggle - 切换 onOff 变量并将其保存到 AppStorage (UserDefaults)。

AppStorage 只有在没有 GeometryReader 的情况下才能完美运行。请注释掉 3 个标记的行以查看。

这是一个错误吗?还是我的 AppStorage 代码有问题?或者 GeometryReader 部分是错误的?如果我可以将黄色部分设置为全角,我可以完全放弃 GeometryReader。

【问题讨论】:

【参考方案1】:

在我的测试中有效的一个解决方案是排除GeometryReader 的内容,包括@AppStorage

struct ContentView: View 
    var body: some View 
        NavigationView 
            GeometryReader  proxy in
                _ContentView(width: proxy.size.width)
            
        
    


struct _ContentView: View 
    var width: CGFloat
    @AppStorage(wrappedValue: false, Settings.onOff) var onOff

    var body: some View 
        List 
            Section(header: header) 
                Toggle(isOn: $onOff) 
                    Text("ON / OFF")
                
            

        
        .listStyle(GroupedListStyle())
    

    var header: some View 
        VStack 
            VStack 
                Text("THIS SHOULD BE FULL-WIDTH")
                Text("It is thanks to GeometryReader")
            
            .padding()
            .frame(width: width)
            .background(Color.yellow)

            HStack 
                Text("This should update from AppStorage: ")
                Spacer()
                Text(onOff == true ? "ON" : "OFF")
            
            .padding()
        
        .textCase(nil)
        .font(.body)
    

【讨论】:

谢谢,罗伯!它有效:) 所以它似乎是一个错误。 GeometryReader 或 AppStorage 在 SwiftUI 中数据流的工作方式存在一些问题。您的解决方案会强制视图正确刷新。我会将这个错误提交给 Apple。用你的解决方案。非常感谢【参考方案2】:

是的,这绝对是一个错误。看起来AppStorage 由于某种原因与State 的行为不完全相同,并且更改不会触发GeometryReader 内部的更新。示例可以简化为:

struct ContentView: View 
    @AppStorage("onOff") var onOff = false

    var body: some View 
        VStack 
            Text("Text updating: " + (onOff == true ? "ON" : "OFF"))
            GeometryReader  reader in
                Toggle(isOn: $onOff) 
                    Text("Text not updating: " + (onOff == true ? "ON" : "OFF"))
                
            
        
    

这在最新的 iOS 15 测试版中甚至都没有修复。您可以尝试使用一些重复的状态变量来破解,但我建议您切换到 custom AppStorage,它在这种情况下可以正常工作。

【讨论】:

谢谢你,菲利普。我不知道自定义 AppStorage。感谢您的链接。到目前为止,我正在尝试编写一个没有任何依赖关系的代码,所以我想我会提出你的其他建议 - 使用另一个状态变量。但现在 Rob 的解决方案同样有效。 @mallow 我明白了。我不喜欢使用一些巨大的依赖项,但在这种情况下,解决方案源代码只有大约 60 行代码(其余的只是不同类型的样板初始化程序),这对我来说很好。如果需要,我可以轻松地复制和修改它,这与系统不同,并且不需要通过将GeometryReader 移出视图来破解它。但这取决于你。 在我的情况下,移动 GeometryReader 更容易,因为我只有一个地方。除了这个 AppStorage 对我有用。但是很高兴您发布了此链接,因为在其他项目中您的解决方案可能会更好。此外,我对外部库和 Swift 包还没有太多经验:)

以上是关于SwiftUI - AppStorage 不适用于 GeometryReader的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI:绑定到@AppStorage

使用 @AppStorage 进行设置的 SwiftUI 最佳实践:如何在加载 @AppStorage 之前读取 UserDefaults?

SwiftUI @AppStorage 不在函数中刷新

SwiftUI 中的可模拟 @AppStorage

更新单独结构中的@AppStorage 后如何更新 SwiftUI 视图

如何让@AppStorage 在 MVVM / SwiftUI 框架中工作?