【问题标题】:Passing a state variable to parent view将状态变量传递给父视图
【发布时间】:2021-12-01 07:52:48
【问题描述】:

我有以下代码:

struct BookView: View {
    
    @State var title = ""
    @State var author = ""

    var body: some View {
        TextField("Title", text: $title)
        TextField("Author", text: $author)
    }
}
struct MainView: View {
    @State private var presentNewBook: Bool = false
    var body: some View {
        NavigationView {
            // ... some button that toggles presentNewBook
        }.sheet(isPresented: $presentNewBook) {
        let view = BookView()
        view.toolbar {
            ToolbarItem(placement: principal) {
                TextField("Title", text: view.$title)
            }
        }
    }
    }
}

这会编译,但在运行时给我以下错误:

Accessing State's value outside of being installed on a View. This will result in a constant Binding of the initial value and will not update.

如何将状态变量传递给其他外部视图?我不能在BookView 上使用ObservableObject,因为这需要我将它从struct 更改为class

【问题讨论】:

    标签: swift swiftui view state


    【解决方案1】:

    您也可以像这样在视图之间传递状态变量:

    let view = BookView(title: "foobar")
    view.toolbar {
         ToolbarItem(placement: principal) {
             TextField("Title", text: view.$title)
         }
    }
    

    那么,BookView内部:

    @State var title: String
    
    init(title: String) {
        _title = State(initialValue: title)
    }
    

    来源:How could I initialize the @State variable in the init function in SwiftUI?

    【讨论】:

      【解决方案2】:

      一般来说,您的状态应始终在视图层次结构的上层拥有。尝试从父级访问子级状态是一种反模式。

      一种选择是使用@Bindings 将值传递给子视图:

      struct BookView: View {
          
          @Binding var title : String
          @Binding var author : String
          
          var body: some View {
              TextField("Title", text: $title)
              TextField("Author", text: $author)
          }
      }
      
      struct ContentView: View {
          @State private var presentNewBook: Bool = false
          
          @State private var title = ""
          @State private var author = ""
          
          var body: some View {
              NavigationView {
                  VStack {
                      Text("Title: \(title)")
                      Text("Author: \(author)")
                      Button("Open") {
                          presentNewBook = true
                      }
                  }
              }.sheet(isPresented: $presentNewBook) {
                  BookView(title: $title, author: $author)
              }
          }
      }
      

      另一种可能性是使用ObservableObject

      class BookState : ObservableObject {
          @Published var title = ""
          @Published var author = ""
      }
      
      struct BookView: View {
          
          @ObservedObject var bookState : BookState
          
          var body: some View {
              TextField("Title", text: $bookState.title)
              TextField("Author", text: $bookState.author)
          }
      }
      
      struct ContentView: View {
          @State private var presentNewBook: Bool = false
          @StateObject private var bookState = BookState()
          
          var body: some View {
              NavigationView {
                  VStack {
                      Text("Title: \(bookState.title)")
                      Text("Author: \(bookState.author)")
                      Button("Open") {
                          presentNewBook = true
                      }
                  }
              }.sheet(isPresented: $presentNewBook) {
                  BookView(bookState: bookState)
              }
          }
      }
      

      我对您的示例视图进行了一些更改,因为对我来说结构不清楚,但在父级别拥有状态的概念是重要的元素。

      【讨论】:

      • @dsynkd 你昨天添加了一条关于你想要达到的目标的评论,但现在它已被删除。还有什么我可以在此处添加的内容足以澄清您接受答案的内容吗?
      • @jpndx 对不起!我删除了评论,因为我认为这可能是一个愚蠢的问题。您的回答很好,我对此表示赞同,但这并不是我想要的。我不想使用 ObservableObject,因为我必须创建一个状态类,并且我想尽量避免增加的复杂性。而且我不想使用绑定,因为这意味着在我将绑定传递给的类中将所有变量作为 State 重复。我选择了我在下面发布的内容。你怎么看?
      • 您的答案与您在问题中提供的代码不兼容。它失败并显示“'$title' 由于'私人'保护级别而无法访问”。对我来说,这似乎是 SwiftUI 中的一种反模式。
      • 谢谢你,我已经解决了。我实际上是在尝试通过将要编辑的对象传入视图来初始化“编辑视图”。我想使用状态包装器来获得反应视图属性。我能想到的唯一其他选择是使用环境对象,例如 currentBook,它是在初始化编辑视图之前设置的。但我不确定这是否会更好。在这种情况下,您会建议 SwiftUI 中的正确模式是什么?
      • 到目前为止,使用绑定是实现这一目标的最常用方法。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-08
      • 2011-09-05
      • 2012-04-25
      • 2019-10-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多