将绑定传递回父级的父级视图

Posted

技术标签:

【中文标题】将绑定传递回父级的父级视图【英文标题】:Passing binding back to parent's parent view 【发布时间】:2021-04-14 00:44:19 【问题描述】:

我有 4 个视图。

    祖父母 父母 孩子 编辑视图

Grandparent 有一个指向 Parent 的导航链接,Parent 有一个指向 Child 的导航链接。 Child 有一个按钮,该按钮通过 Parent 中的绑定和 Child 中的绑定从 Grandparent 初始化 @State 变量位置(类)。同一个按钮还更新了来自祖父的@State 变量 showEditView(再次通过绑定),它显示了 EditView。

目前有 11 行被注释掉。如果它们被注释掉,当我点击子视图中的按钮时,应用程序会抛出“致命错误:意外发现 nil ...”错误。如果任一部分未注释,则该按钮不会引发错误。

如果我将绑定位置传递给 EditView,而不是像我当前正在做的那样将属性本身传递给它,它也可以工作,然后将其包装为 @ObservedObject。

我不明白这里发生了什么。我唯一能做到的是,当它工作时,SwiftUI 正在更新 location 属性,因为它在正文中使用。如果是这种情况,这似乎表明我每次想要以这种方式传递属性时都必须包含该属性的隐藏文本视图。

祖父母

import SwiftUI

struct Grandparent: View 
    @State var location: Location!
    @State var showEditView: Bool = false
    
    var body: some View 
        NavigationView 
            VStack
                NavigationLink(
                    destination: Parent(location: $location, showEditView: $showEditView)) 
                    Text("Navigate")
                
// //               section 1
//                if location != nil 
//                    Text(location.name)
//                 else 
//                    Text("No location yet")
//                
            
// //            section 2
//            .navigationBarItems(trailing:
//                Button("Edit")
//                    showEditView = true
//                
//                .disabled(location == nil)
//            )
        
        .padding()
        .sheet(isPresented: $showEditView) 
            EditView(placemark: location, dismiss:  showEditView = false )
        
    

父母

import SwiftUI

struct Parent: View 
    @Binding var location: Location!
    @Binding var showEditView: Bool
    
    var body: some View 
        NavigationLink(
            destination: Child(location: $location, showEditView: $showEditView),
            label: 
                Text("Child")
            )
    

孩子

import SwiftUI

struct Child: View 
    @Binding var location: Location!
    @Binding var showEditView: Bool
    var body: some View 
        Button("Make location") 
            location = Location(name: "Lebanon")
            showEditView = true
        
    

编辑视图

import SwiftUI

struct EditView: View 
    @ObservedObject var placemark: Location
    var dismiss: () -> Void
    
    var body: some View 
        NavigationView 
            Text(placemark.name)
                .navigationTitle("Edit place")
                .navigationBarItems(trailing: Button("Done")  dismiss() )
        
    

位置

import Foundation

class Location: ObservableObject 
    init(name: String) 
        self.name = name
    
    
    @Published var name: String

【问题讨论】:

你有没有试过不使用强制换行可选@State var location: Location? 您可能还想要@StateObject 而不是@State....而且不初始化它也没有意义 【参考方案1】:

您可能不应该将您的Location 声明为!,然后对它进行nil 检查。使用! 有点要求发生崩溃。我认为您遇到的是在设置location 之前呈现sheetwhen 在运行循环中没有任何保证会设置@State 变量,因此最好考虑到它是nil 的情况(绝对不要使用! 强制打开它)。

其次,至少考虑到您在这里的情况,您可能不应该将class 用于Location - 它应该只是struct

最终,您会遇到一点复杂性,因为根据您的视图名称判断,您想在某个时候编辑Location。使用Optional 会变得有点棘手,因为像TextField 这样的东西需要非可选值,但这可以通过各种方式解决(参见我在哪里使用nonNilBinding)。

这样的事情绝对比你目前正在做的更安全。它可能不是完全你想要的,但希望它能让你走上正确的道路。


struct Location 
    var name : String


struct Grandparent: View 
    @State var location: Location?
    @State var showEditView: Bool = false
    
    var body: some View 
        NavigationView 
            VStack
                NavigationLink(
                    destination: Parent(location: $location, showEditView: $showEditView)) 
                    Text("Navigate")
                
                if let location = location 
                    Text(location.name)
                 else 
                    Text("No location yet")
                
            
            .padding()
            .sheet(isPresented: $showEditView) 
                EditView(placemark: $location, dismiss:  showEditView = false )
            
        
    



struct Parent: View 
    @Binding var location: Location?
    @Binding var showEditView: Bool
    
    var body: some View 
        NavigationLink(
            destination: Child(location: $location, showEditView: $showEditView),
            label: 
                Text("Child")
            )
    


struct Child: View 
    @Binding var location: Location?
    @Binding var showEditView: Bool
    var body: some View 
        Button("Make location") 
            location = Location(name: "Lebanon")
            showEditView = true
        
    


struct EditView: View 
    @Binding var placemark: Location?
    var dismiss: () -> Void
    
    var nonNilBinding : Binding<Location> 
        .init  () -> Location in
            placemark ?? Location(name:"Default")
         set:  (newValue) in
            placemark = newValue
        

    
    
    var body: some View 
        NavigationView 
            TextField("Name", text: nonNilBinding.name)
                    .navigationTitle("Edit place")
                    .navigationBarItems(trailing: Button("Done")  dismiss() )
        
    


【讨论】:

当我第一次读到它们时,我认为隐式展开的可选项很愚蠢,但我比我预期的更快地遇到了一个用例,这基本上就是我想要在这里复制的内容。使用 MapKit,我有一个注释(我在这里使用的位置)。它是隐式展开的,因为当视图加载时,它是 nil。它只有在创建后才会被编辑,就像我在这里处理位置一样。调试时,我看到创建了位置,但是,就像你说的,“在设置位置之前渲染了工作表。” 这就是为什么 location 是一个类——它只是我真正想要的东西的替代品,它是一个 MKPointAnnotation,是的,虽然很痛苦,但可以通过一个扩展和一个带有 getter 和 setter 的计算属性。虽然现在我说的是可选对象上的可选属性,所以还是有点不一样。 隐式展开的选项似乎是一个相当冒险的计划。关于注释,我一直处理这些注释的方式是为地图动态生成它们——而不是持有对它们的引用。

以上是关于将绑定传递回父级的父级视图的主要内容,如果未能解决你的问题,请参考以下文章

如何在 React 中将状态传递回父级?

React 功能组件 - 当组件返回元素数组时,如何将 refs 传递回父级?

jquery如何获得父级的父级元素?

将多个子复选框绑定到一个父级

使用后退按钮导航到 Android Activity 父级的父级 [重复]

Apollo Server Stitching - 在解析器中获取父级的父级