Home > Mobile >  SwiftUI Form with Picker - NavigationView creating additional back function
SwiftUI Form with Picker - NavigationView creating additional back function

Time:10-07

I'm adding a side loading view to an app which is used for view and adding certain incidents. The basic layout is intended as follows:

enter image description here

Here is the code for this content:

   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($0)
                        }
                    }
                }.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)
}

}

I intent to obviously split this code out, however I am confused with the way the Form is working here inside the NavigationView. As you can see, I have a back arrow at the top of the part which leads back to a view which contains just the section header. However, I do not want to navigate backwards from the point. What I intend is to have the section header and form on this main page and the NavigationView is just to enable to picker to work, which it does as follows:

enter image description here

But how can remove the initial back arrow and display the section header on the first view?

CodePudding user response:

I have two potential answers. The first is fixing what you were trying to do. It works, but it is hacky and I would only use it at the end of a view hierarchy, if at all. I mocked your code into a minimal, reproducible example:

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($0)
                                }
                            }
                        }.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
}

The second, is flexible, reusable and not hacky in the least:

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
}

Use it like this in 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()
            }
        }
    }
}

You may notice that because the views are in a VStack, that the biggest the IncidentForm will get is half of the screen. However, if you put it in a ZStack it will limit itself as you requested. Use it how you need.

  • Related