SwiftUI通过另一个视图实例设置状态变量

时间:2020-05-27 12:52:08

标签: swiftui

在SwiftUI中,我创建了一个结构,该结构应根据某些状态变量创建不同的覆盖视图。如果任何状态布尔值都为true,则它应返回自定义视图(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()
            }
        }
    }
}

现在,我已经在主视图中的某些内容上实现了叠加层,该按钮应该会更改状态并显示正确的叠加层,例如:

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,并且更改状态应尽可能短而整洁。到处定义状态就像代码重复一样。也许我需要一个完全不同的体系结构?

2 个答案:

答案 0 :(得分:0)

应改为使用绑定。这是可能的解决方案。

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))
    }
}

答案 1 :(得分:0)

要使其成为您想要的方式,请使用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)
    }
}