【问题标题】:SwiftUI set state variables through another view instanceSwiftUI 通过另一个视图实例设置状态变量
【发布时间】:2020-09-14 12:30:10
【问题描述】:

在 SwiftUI 中,我创建了一个结构,它应该根据一些状态变量创建不同的覆盖视图。如果任何状态布尔值为真,那么它应该返回自定义视图(ErrorOverlayLoadingOverlayEmptyView),如下所示:

struct OverlayContainer: View {
    @State var isLoading: Bool = false
    @State var isErrorShown: Bool = false

    func setIsLoading(isLoading: Bool) {
        self.isLoading = isLoading
    }

    func setIsErrorShown(isErrorShown: Bool) {
        self.isErrorShown = isErrorShown
    }

    var body: some View {
        Group {
            if(isErrorShown) {
                ErrorOverlay()
            }
            else if(isLoading) {
                LoadingOverlay()
            }
            else {
                EmptyView()
            }
        }
    }
}

现在我已经在 Home 视图中的某些内容上实现了覆盖,其中的按钮应该会改变状态并显示正确的覆盖,如下所示:

struct Home: View {

    var body: some View {
        let overlayContainer = OverlayContainer()

        return HStack {
            // Some more content here

            Button(action: {
                overlayContainer.setIsLoading(isLoading: true)
            }) {
               Text("Start loading")
            }
            Button(action: {
                overlayContainer.setIsErrorShown(isErrorShown: true)
            }) {
               Text("Show error")
            }
        }.overlay(overlayContainer)
    }
}

这不起作用:当我单击按钮时,什么也没有发生。为什么以及如何解决这个问题? (不使用绑定,见下文)


ps。通过执行以下操作,我已经能够获得有效的解决方案:

  1. 将状态布尔值提取到主页视图
  2. 通过 OverlayContainer 的构造函数传递这些
  3. 单击按钮时更改状态布尔值而不是调用 set 方法
  4. 更改 OverlayContainer,使其实现一个带有两个布尔值的 init 方法
  5. 将 OverlayContainer 中的状态布尔值更改为绑定。

但是,我想实现 OverlayContainer 中的状态,以便能够在不同的屏幕中重新使用它,而无需在所有这些屏幕中实现状态变量。首先是因为可能会有比这 2 种更多的情况。其次因为不是所有的屏幕都需要访问所有的状态,而且我还没有找到一种通过 init 方法实现可选绑定的简单方法。

在我看来,所有这些状态都属于 OverlayContainer,并且更改状态应该尽可能短且干净。到处定义状态感觉就像代码重复。也许我需要一个完全不同的架构?

【问题讨论】:

  • 您找到解决方案了吗?我有同样的问题,下面的建议不能满足您将状态保持在“内部”对象内的要求......

标签: swiftui


【解决方案1】:

应该用Binding代替。这是可能的解决方案。

struct OverlayContainer: View {
    @Binding var isLoading: Bool
    @Binding var isErrorShown: Bool

    var body: some View {
        Group {
            if(isErrorShown) {
                ErrorOverlay()
            }
            else if(isLoading) {
                LoadingOverlay()
            }
            else {
                EmptyView()
            }
        }
    }
}

struct Home: View {
    @State var isLoading: Bool = false
    @State var isErrorShown: Bool = false

    var body: some View {
        HStack {
            // Some more content here

            Button(action: {
                self.isLoading = true
            }) {
               Text("Start loading")
            }
            Button(action: {
                self.isErrorShown = true
            }) {
               Text("Show error")
            }
        }.overlay(OverlayContainer(isLoading: $isLoading, isErrorShown: $isErrorShown))
    }
}

【讨论】:

    【解决方案2】:

    要按照您想要的方式进行操作,请使用 Binding:

    struct OverlayContainer: View {
    
        @Binding var isLoading: Bool
        @Binding var isErrorShown: Bool
    
        func setIsLoading(isLoading: Bool) {
            self.isLoading = isLoading
            self.isErrorShown = !isLoading
        }
    
        func setIsErrorShown(isErrorShown: Bool) {
            self.isErrorShown = isErrorShown
            self.isLoading = !isErrorShown
        }
    
        var body: some View {
            Group {
                if(isErrorShown) {
                    ErrorOverlay()
                }
                else if(isLoading) {
                    LoadingOverlay()
                }
                else {
                    EmptyView()
                }
            }
        }
    }
    
    struct Home: View {
    
        @State var isLoading = false
        @State var isErrorShown = false
    
        var body: some View {
    
            let overlayContainer = OverlayContainer(isLoading: $isLoading, isErrorShown: $isErrorShown)
    
            return HStack {
                // Some more content here
    
                Button(action: {
                    overlayContainer.setIsLoading(isLoading: true)
    
                }) {
                    Text("Start loading")
                }
                Button(action: {
                    overlayContainer.setIsErrorShown(isErrorShown: true)
                }) {
                    Text("Show error")
                }
            }.overlay(overlayContainer)
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-06-09
      • 1970-01-01
      • 2020-07-21
      • 1970-01-01
      • 2012-08-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多