带选择器的 SwiftUI 表单 - NavigationView 创建额外的返回功能

SwiftUI Form with Picker - NavigationView creating additional back function

我正在向用于查看和添加某些事件的应用程序添加侧面加载视图。基本布局如下:

这是此内容的代码:

   var body: some View {
    List {
        Section(header: Text("Existing incidents")) {
            ForEach(incidents) { incident in
                
                VStack(alignment: .leading) {
                    HStack {
                        Image.General.incident
                            .foregroundColor(Constants.iconColor)
                        Text(incident.name)
                            .foregroundColor(Constants.textColor)
                            .bold()
                    }
                    Spacer()
                    HStack {
                        Image.General.clock
                            .foregroundColor(Constants.iconColor)
                        Text(incident.date)
                            .foregroundColor(Constants.textColor)
                    }
                    Spacer()
                    HStack {
                        Image.General.message
                            .foregroundColor(Constants.iconColor)
                        Text(incident.message)
                            .foregroundColor(Constants.textColor)
                    }
                }
            }
        }
    }
    .listStyle(PlainListStyle())
    
    NavigationView {
        Section(header: Text("Add an incident")
                    .bold()
                    .foregroundColor(Constants.textColor)
                    .padding()) {
            Form {
                HStack {
                    Image.General.select
                    Spacer()
                    Picker("Incident selection", selection: $selectedIncident)
                    {
                        ForEach(incidentsList, id: \.self) {
                            Text([=10=])
                        }
                    }
                }.padding()
                
                HStack {
                    Image.General.write
                    Spacer()
                    TextField("Additional comments", text: $comments)
                }.padding()
                
                Button {
                    
                }
                
            label: {
                Text("Submit")
                    .foregroundColor(Color(UIColor.white))
            }
            .padding(.top, 5)
            .padding(.bottom, 5)
            .padding(.leading, 20)
            .padding(.trailing, 20)
            .background(Color(UIColor.i6.blue))
            .cornerRadius(5)
            }
        }
    }
    .padding(.bottom)
}

}

我显然打算将这段代码分开,但是我对表单在 NavigationView 中的工作方式感到困惑。如您所见,我在该部分的顶部有一个后退箭头,可以返回到仅包含部分 header 的视图。但是,我不想从该点向后导航。我打算在这个主页上有 header 部分和表单,而 NavigationView 只是为了使选择器能够工作,它的作用如下:

但是如何删除初始后退箭头并在第一个视图中显示部分 header?

我有两个可能的答案。首先是修复您尝试做的事情。它有效,但它很老套,我只会在视图层次结构的末尾使用它,如果有的话。我将您的代码模拟成一个最小的、可重现的示例:

struct SplitViewFormPicker: View {
    @State var incidents = [
        Incident(name: "Incident 1", date: "1/1/21", message: "message 1 is here"),
        Incident(name: "Incident 2", date: "1/2/21", message: "message 2 is here"),
        Incident(name: "Incident 3", date: "1/3/21", message: "message 3 is here"),
        Incident(name: "Incident 4", date: "1/4/21", message: "message 4 is here"),
        Incident(name: "Incident 5", date: "1/5/21", message: "message 5 is here"),
        Incident(name: "Incident 6", date: "1/6/21", message: "message 6 is here"),
        Incident(name: "Incident 7", date: "1/7/21", message: "message 7 is here"),
        Incident(name: "Incident 8", date: "1/8/21", message: "message 8 is here"),
        Incident(name: "Incident 9", date: "1/9/21", message: "message 9 is here"),
        Incident(name: "Incident 10", date: "1/10/21", message: "message 10 is here"),
        Incident(name: "Incident 11", date: "1/11/21", message: "message 11 is here"),
    ]
    
    @State var selectedIncident = ""
    @State var comments = ""
    let incidentsList = ["Incident 1", "Incident 2", "Incident 3", "Incident 4"]
    
    var body: some View {
        VStack {
            List {
                Section(header: Text("Existing incidents")) {
                    ForEach(incidents) { incident in
                        
                        VStack(alignment: .leading) {
                            HStack {
                                Image(systemName: "exclamationmark.triangle.fill")
                                    .foregroundColor(Constants.iconColor)
                                Text(incident.name)
                                    .foregroundColor(Constants.textColor)
                                    .bold()
                            }
                            Spacer()
                            HStack {
                                Image(systemName: "clock")
                                    .foregroundColor(Constants.iconColor)
                                Text(incident.date)
                                    .foregroundColor(Constants.textColor)
                            }
                            Spacer()
                            HStack {
                                Image(systemName: "message.fill")
                                    .foregroundColor(Constants.iconColor)
                                Text(incident.message)
                                    .foregroundColor(Constants.textColor)
                            }
                        }
                    }
                }
            }
            .listStyle(PlainListStyle())
            
            NavigationView {
                Form {
                    Section(header: Text("Add an incident")
                                .bold()
                                .foregroundColor(Constants.textColor)
                                .padding()) {
                        HStack {
                            Image(systemName: "hand.draw")
                            Spacer()
                            Picker("Incident selection", selection: $selectedIncident)
                            {
                                ForEach(incidentsList, id: \.self) {
                                    Text([=10=])
                                }
                            }
                        }.padding()
                        
                        HStack {
                            Image(systemName: "rectangle.and.pencil.and.ellipsis")
                            Spacer()
                            TextField("Additional comments", text: $comments)
                        }.padding()
                        
                        Button {
                            
                        }
                        
                    label: {
                        Text("Submit")
                            .foregroundColor(Color(UIColor.white))
                    }
                    .padding(.top, 5)
                    .padding(.bottom, 5)
                    .padding(.leading, 20)
                    .padding(.trailing, 20)
                    .background(Color(UIColor.systemGray6))
                    .cornerRadius(5)
                    }
                                .navigationBarHidden(true)
                }
            }
            .padding(.bottom)
        }
    }
}

struct Constants {
    static var textColor = Color.black
    static let iconColor = Color.orange
}

struct Incident: Identifiable {
    let id = UUID()
    let name: String
    let date: String
    let message: String
}

第二个是灵活的、可重复使用的,而且一点也不难用:

struct PartialSheet<Content: View>: View {
    
    var sheetSize: SheetSize
    let content: () -> Content
    
    init(_ sheetSize: SheetSize, @ViewBuilder content: @escaping () -> Content) {
        self.sheetSize = sheetSize
        self.content = content
    }
    
    var body: some View {
        content()
            .frame(height: sheetHeight())
            .animation(.spring(), value: sheetSize)
    }
    
    private func sheetHeight() -> CGFloat {
        switch sheetSize {
        case .quarter:
            return UIScreen.main.bounds.height / 4
        case .half:
            return UIScreen.main.bounds.height / 2
        case .threeQuarter:
            return UIScreen.main.bounds.height * 3 / 4
        case .full:
            return .infinity
        }
    }
}

enum SheetSize: Equatable {
    case quarter, half, threeQuarter, full
}

在 SplitViewFormPicker 中这样使用:

    NavigationView {
        VStack {
            List {
            // The same code as above not repeated to save space
            }
            .listStyle(PlainListStyle())
        
            // Call PartialSheet, tell it what size you want, and pass the view as a closure.
            // It will return that view sized to the screen.
            PartialSheet(.half) {
                IncidentForm()
            }
        }
    }
}

您可能会注意到,由于视图在 VStack 中,因此 IncidentForm 将获得的最大屏幕的一半。但是,如果您将它放在 ZStack 中,它会根据您的要求限制自己。根据需要使用它。