使用 @State 变量不适用于从子视图更改 Body 中的视图状态

Posted

技术标签:

【中文标题】使用 @State 变量不适用于从子视图更改 Body 中的视图状态【英文标题】:Using @State variable does not work for changing view state in Body from child view 【发布时间】:2021-06-20 12:44:38 【问题描述】:

假设在我的 ios SwiftUI 应用中有一个简单的 ContentView,里面有两个视图。

对 ContentView 的引用可用于代码的其他部分。

从那里,用户想要确定显示两个视图中的哪一个,即一个、不显示或两者都显示。

那个代码有点像

contentViewReference.setView1Visible(view1Visible:true) //or false

这是内容视图

struct ViewsState

    var view1Visible:Bool
    var view2Visible:Bool
    init(view1Visible:Bool,view2Visible:Bool)
       
          self.view1Visible=view1Visible
          self.view2Visible=view2Visible
       
 

struct ContentView: View 
 
    @State private var viewsState:ViewsState=ViewsState(view1Visible:true,view2Visible:false)

    
    public func setView1Visible(view1Visible:Bool)
        
          viewsState.view1Visible=view1Visible
        

    public func setView2Visible(view2Visible:Bool)
        
          viewsState.view2Visible=view2Visible
        

    var body: some View 
    
        if (viewsState.view1Visible) View1
        if (viewsState.view1Visible) View2
    

但是视图的可见性永远不会改变,为什么?

我认为我不需要绑定,因为状态“真相”在 ContentView 内部,但它是通过公共函数分配的。

没有父视图,切换代码确实在 View1 的菜单中。

但是打电话的时候

setView1Visible(view1Visible:false)

比如没有效果,View1 不会被隐藏。

这只是一段代码。 Real ContentView 稍微复杂一些,但问题没有改变。

【问题讨论】:

嗨 P5Music,视图的可见性取决于两个单独的条件,还是可见性取决于一个条件?我想知道是一个可见还是两者都可见或不可见?其次,什么决定了知名度?是用户点击还是其他条件? @MacUserT 在真正的应用程序中,我将其用作一种导航系统,所以是的,理想情况下,当 View1 被隐藏时,View2 就会变得可见。我知道它是实验性的,但我需要知道它是如何工作的,它没有理由不起作用,即使作为练习也是如此。 @State 应为大写 如果两者都是false,则很可能需要else 中的某些视图。 另外,您不能从另一个Viewclassstruct 访问View 中的方法,理想情况下,用户将直接访问您的ViewState 而不是ContentView跨度> 【参考方案1】:
class ViewsState: ObservableObject

    @Published var view1Visible:Bool
    @Published var view2Visible:Bool
    init(view1Visible:Bool,view2Visible:Bool)
    
        self.view1Visible=view1Visible
        self.view2Visible=view2Visible
    
    public func setView1Visible(view1Visible:Bool)
    
        self.view1Visible = view1Visible
        view2Visible = !view1Visible
    
    
    public func setView2Visible(view2Visible:Bool)
    
        self.view2Visible = view2Visible
        view1Visible = !view2Visible
    


struct ViewStateView: View 
    @StateObject private var viewsState: ViewsState = ViewsState(view1Visible:true,view2Visible:false)

    var body: some View
    
        VStack
            if (viewsState.view1Visible) Text("View1")
            if (viewsState.view2Visible) Text("View2")
            SomeOtherView().environmentObject(viewsState)
        
    

struct SomeOtherView: View 
    @EnvironmentObject var viewsState: ViewsState
    var body: some View
    
        VStack
            Text("SomeOtherView")
            Button("change state view 1", action: 
                viewsState.setView1Visible(view1Visible:false)
            )
            Button("change state view 2", action: 
                viewsState.setView2Visible(view2Visible:false)
            )
        
        
    

struct ViewStateView_Previews: PreviewProvider 
    static var previews: some View 
        ViewStateView()
    

【讨论】:

我得到“没有找到 ViewsState 类型的 ObservableObject。ViewsState 的 View.environmentObject(_:) 作为此视图的祖先可能会丢失”无论我使用什么方法来“创建”和注入该对象在 Views 中,从 ContentView 等开始。在 Views 层次结构中拥有一个“实时”的 Observable 和 State 对象的解决方案是什么? 你必须在启动视图时添加这个 .environmentObject() 看看 SomeOtherView 在 body 中初始化 我有一些视图,一个叫做 mainView,但它在 ContentView 中,但它需要一个自身的 StateObject,在运行时它说每次都会创建一个新实例,但是如果我使用 environmentObject 代替,它表示祖先中没有可观察的对象。 表示初始化时需要 environmentObject 修饰符的视图。你必须把它传下去。在某些情况下,例如表格不会自动传递。在 SO 中多次询问过同样的错误 我确保我的代码与您的代码相似,只是在 mainView 的主体中,它有一个返回 EmptyView 的 func 调用,用于设置一些 viewsState 成员,以便在下面立即使用它们。该函数只是将一个子视图设置为可见,另一个隐藏。【参考方案2】:

@P5music,没有足够的代码来复制您的问题并正确调查。 另外我不知道你是怎么得到的:

   contentViewReference.setView1Visible(view1Visible:true) 

以下是对您的代码的一些观察。

    @State private var viewsState:ViewsState(view1Visible:true,view2Visible:false)

应该是

    @State var viewsState = ViewsState(view1Visible:true,view2Visible:false)

你也可能想要:

    var body: some View 

    if (viewsState.view1Visible) View1
    if (viewsState.view2Visible) View2  // <--- here 

另外,“...确定显示两个视图中的哪一个”,这意味着每次设置 “view1Visible”设置为某个值,同时还需要将“view2Visible”设置为相反的值。

【讨论】:

别忘了大写@State 是的,谢谢,我刚刚更新了答案/评论。 @workingdog 现在哪个视图可见或不可见并不重要,这应该可以工作。如何解决?【参考方案3】:

假设您的“contentViewReference.setView1Visible(view1Visible:true)”有效,以下 测试代码似乎可以工作:

import SwiftUI

@main
struct TestApp: App 
    var body: some Scene 
        WindowGroup 
            ContentView()
        
    


struct ViewsState
    var view1Visible:Bool
    var view2Visible:Bool
    init(view1Visible:Bool, view2Visible:Bool)
        self.view1Visible = view1Visible
        self.view2Visible = view2Visible
    


struct ContentView: View 
    @State var viewsState = ViewsState(view1Visible:true, view2Visible:false)
    
    public func setView1Visible(view1Visible:Bool)
        viewsState.view1Visible = view1Visible
        viewsState.view2Visible = !view1Visible
    
    
    public func setView2Visible(view2Visible:Bool)
        viewsState.view2Visible = view2Visible
        viewsState.view1Visible = !view2Visible
    
    
    var body: some View 
        VStack 
            Button("change view") 
                if viewsState.view1Visible 
                    setView1Visible(view1Visible: false)
                 else 
                    setView1Visible(view1Visible: true)
                
            
            if (viewsState.view1Visible) View1()
            if (viewsState.view2Visible) View2()
        
    


struct View1: View 
    var body: some View 
        Text("View1")
    


struct View2: View 
    var body: some View 
        Text("View2")
    

【讨论】:

以上是关于使用 @State 变量不适用于从子视图更改 Body 中的视图状态的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI:从子视图中关闭模式

更改集合视图的 isHidden 属性不适用于搜索栏取消按钮

@state 变量更改后视图不更新

通过更改 SwiftUI 中的 @State 变量来刷新视图不起作用

动态 PHP 变量不适用于使用 mPDF 的 codeigniter php 视图文件

从子视图中更改视图中的数组“不能在不可变值上使用变异成员:'self' 是不可变的”