G. Marc
G. Marc

Reputation: 6037

How to add a navigation bar button to a picker

The user should be able to add new values to a picker if the desired option is not already there. So I basically want to implement an "Add" button to the view with the options.

The only way I get this to work is by adding the .navigationBarItems modifier to each option. But this seems not only an overhead, but it's also a problem if the list doesn't contain any options at the beginning (then the modifier isn't applied to any element and therefore the button isn't visible).

I already tried a couple of other things:

  1. Adding the modifier to the ForEach is merging all options into one button, so individual options aren't selectable anymore.
  2. Adding an EmptyView bellow Picker and attaching the modifier to it results in an extra empty line in the list.

import SwiftUI

struct ContentView: View {
    @State private var selection = 1
    let options = [Option(title: "Option 1", id: 1), Option(title: "Option 2", id: 2)]
    
    struct Option {
        var title: String
        var id: Int
    }

    var body: some View {
        NavigationView {
            Form {
                Picker("Test", selection: $selection) {
                    ForEach(options, id:\.id) { option in
                        Text(option.title).tag(option.id)
                        .navigationBarItems(trailing: Text("Add"))
                    }
                    .navigationBarTitle("Options", displayMode: .inline)
                }
                .navigationBarTitle("Form")
                .navigationBarItems(trailing: EmptyView())
            }
        }
    }
}

Upvotes: 0

Views: 627

Answers (1)

Asperi
Asperi

Reputation: 258443

Changing diaplayMode on the fly always gives undesired effect, but if you'd have same one (or none for Options) then the following approach could work.

Tested with Xcode 11.4 / iOS 13.4

demo

struct ContentView: View {
    @State private var selection = 1
    @State private var options = [Option(title: "Option 1", id: 1), Option(title: "Option 2", id: 2)]

    struct Option {
        var title: String
        var id: Int
    }

    var body: some View {
        NavigationView {
            Form {
                Picker("Test", selection: $selection) {
                    ForEach(options, id:\.id) { option in
                        self.optionRow(for: option)
                    }
                    .listRowInsets(EdgeInsets(top: 1, leading: 1, bottom: 1, trailing: 1))
                }
            }
            .navigationBarTitle("Form")
//            .navigationBarTitle("Form", displayMode: .inline)
        }
    }

    @ViewBuilder
    func optionRow(for item: Option) -> some View {
        if item.id == selection {
            Text(item.title)
        } else {
            Text(item.title)
                .navigationBarTitle("Options")
//                .navigationBarTitle("Options", displayMode: .inline)
                .navigationBarItems(trailing: Button("Add") {
                    // example of creating new option
                    self.options.append(Option(title: "Option 3", id: 3))
                })
        }
    }
}

Upvotes: 1

Related Questions