Ios 将绑定传递回父级';父视图

Ios 将绑定传递回父级';父视图,ios,swiftui,Ios,Swiftui,我有4种观点 祖父母 母公司 孩子 编辑视图 祖父母有一个指向父母的导航链接,而父母有一个指向孩子的导航链接。Child有一个按钮,通过父级中的绑定和子级中的绑定从祖父母那里初始化@State变量location(一个类)。该按钮还更新了祖父母的@State变量showEditView(再次通过绑定),其中显示了EditView 目前有11行被注释掉了。如果它们被注释掉,当我点击子视图中的按钮时,应用程序会抛出一个“致命错误:意外发现零…”错误。如果任一部分未注释,则按钮不会引发错误 如果我将绑

我有4种观点

  • 祖父母
  • 母公司
  • 孩子
  • 编辑视图
  • 祖父母有一个指向父母的导航链接,而父母有一个指向孩子的导航链接。Child有一个按钮,通过父级中的绑定和子级中的绑定从祖父母那里初始化@State变量location(一个类)。该按钮还更新了祖父母的@State变量showEditView(再次通过绑定),其中显示了EditView

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

    如果我将绑定位置传递给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 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
    }
    

    您可能不应该将您的
    位置
    声明为
    然后执行
    nil
    检查。使用
    是一种要求崩溃发生的行为。我认为您遇到的是在设置
    位置之前渲染
    工作表
    。对于在运行循环中何时设置
    @State
    变量没有任何保证,因此最好考虑
    为nil
    的情况(并且绝对不要使用
    强制展开它)

    其次,至少考虑到这里的场景,您可能不应该为
    位置使用
    ——它应该是一个
    结构

    最终,您将遇到一点复杂性,因为根据视图的名称判断,您希望在某个时候编辑
    位置。使用
    可选
    ,这变得有点棘手,因为
    文本字段
    之类的内容需要非可选值,但这可以在各种was中解决(请参阅我使用的
    非零绑定

    这样做肯定比你现在做的更安全。这可能不是你想要的,但希望它能让你走上正确的道路

    
    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() })
            }
        }
    }
    
    
    
    结构位置{
    变量名称:String
    }
    结构祖父母:视图{
    @状态变量位置:位置?
    @状态变量showEditView:Bool=false
    var body:一些观点{
    导航视图{
    VStack{
    导航链接(
    目的地:父级(位置:$location,showEditView:$showEditView)){
    文本(“导航”)
    }
    如果let location=location{
    文本(location.name)
    }否则{
    文本(“还没有位置”)
    }
    }
    .padding()
    .sheet(显示:$showEditView){
    EditView(placemark:$location,dismise:{showEditView=false})
    }
    }
    }
    }
    结构父级:视图{
    @绑定变量位置:位置?
    @绑定变量showEditView:Bool
    var body:一些观点{
    导航链接(
    目的地:子级(位置:$location,showEditView:$showEditView),
    标签:{
    文本(“儿童”)
    })
    }
    }
    结构子级:视图{
    @绑定变量位置:位置?
    @绑定变量showEditView:Bool
    var body:一些观点{
    按钮(“制作位置”){
    地点=地点(名称:“黎巴嫩”)
    showEditView=true
    }
    }
    }
    结构编辑视图:视图{
    @绑定变量placemark:位置?
    var DISCLISE:()->Void
    var nonNilBinding:Binding{
    .init{()->中的位置
    placemark??位置(名称:“默认”)
    }集合:{(newValue)在
    placemark=newValue
    }
    }
    var body:一些观点{
    导航视图{
    TextField(“名称”,文本:nonNilBinding.Name)
    .navigationTitle(“编辑位置”)
    .navigationBarItems(尾部:按钮(“完成”){disease()})
    }
    }
    }
    
    您是否尝试过不使用强制换行可选的
    @State var location:location?
    您可能还想要
    @StateObject
    而不是
    @State
    。。。。我第一次读到它们的时候,我认为隐式展开可选的是愚蠢的,但是我比我预期的更早地发现了一个用例,这就是我在这里试图复制的。使用MapKit,我有一个注释(我在这里使用location)。它是隐式展开的,因为当视图加载时,它是零。它只有在创建之后才会被编辑,就像我在这里处理位置一样。调试时,我看到创建了位置,但是,正如您所说,“在设置位置之前呈现工作表。”这就是为什么位置是一个类——它只是我真正想要的东西的替代品,这是一个MKPointAnnotation,是的,这是一个麻烦,使用getter和setter通过扩展和计算属性进行更新。虽然我现在谈论的是可选对象上的可选属性,但这有点不同。隐式展开的选项看起来是一个相当冒险的计划。关于注释,我一直处理这些注释的方法是为地图动态生成它们——而不是保存对它们的引用。
    
    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() })
            }
        }
    }