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



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

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

这是行不通的:当我单击按钮时,没有任何反应。为什么以及如何解决这个问题?(不使用绑定,见下文(


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

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

但是,我想在 OverlayContainer 中实现状态,以便能够在不同的屏幕中重用它,而无需在所有这些屏幕中实现状态变量。首先是因为可能有更多的案例,而不仅仅是这两个。其次,因为并非所有屏幕都需要访问所有状态,而且我还没有找到通过 init 方法实现可选绑定的简单方法。

对我来说,感觉所有这些状态都属于 OverlayContainer,更改状态应该尽可能简短和干净。在任何地方定义状态感觉就像代码重复。也许我需要一个完全不同的架构?

它应该改用绑定。这是可能的解决方案。

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

若要使其符合所需方式,请使用 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)
}
}

最新更新