0

I'm working on a SwiftUI project and am having trouble getting a picker to work correctly.

I've got a hierarchy of views split into multiple files with the initial view wrapping everything in a NavigationView.

Looks something like this:

MainFile (TabView -> NavigationView)
- ListPage (NavigationLink)
-- DetailHostPage (Group.EditButton)
if editing
--- DetailViewPage
else
--- DetailEditPage (picker in a form)

The picker that I have in the DetailEditPage does not let me change it's value, though it does display the correct current value.

Picker(selection: self.$_myObj.SelectedEnum, label: Text("Type")) {
    ForEach(MyEnum.allCases, id: \.self) {
        Text("\(String(describing: $0))")
    }
}

If I wrap the picker in a navigation view directly then it works, but now I have a nested navigation view resulting in two back buttons, which is not what I want.

What is causing the picker not to allow it's selection to change, and how can I get it working?

EDIT

Here's an example of how to replicate this:

ContentView.swift

class MyObject: ObservableObject {
    @Published var enumValue: MyEnum

    init(enumValue: MyEnum) {
        self.enumValue = enumValue
    }
}

enum MyEnum: CaseIterable {
    case a, b, c, d
}

struct ContentView: View {
    @State private var objectList = [MyObject(enumValue: .a), MyObject(enumValue: .b)]

    var body: some View {
        NavigationView {
            List {
                ForEach(0..<objectList.count) { index in
                    NavigationLink(destination: Subview(myObject: self.$objectList[index])) {
                        Text("Object \(String(index))")
                    }
                }
            }
        }
    }
}

struct Subview: View {
    @Environment(\.editMode) var mode
    @Binding var myObject: MyObject

    var body: some View {
        HStack {
            if mode?.wrappedValue == .inactive {
                //The picker in this view shows
                SubViewShow(myObject: self.$myObject)
            } else {
                //The picker in this view does not
                SubViewEdit(myObject: self.$myObject)
            }
        }.navigationBarItems(trailing: EditButton())
    }
}

struct SubViewShow: View {
    @Binding var myObject: MyObject

    var body: some View {
        Form {
            Picker(selection: self.$myObject.enumValue, label: Text("enum values viewing")) {
                ForEach(MyEnum.allCases, id: \.self) {
                    Text("\(String(describing: $0))")
                }
            }
        }
    }
}

struct SubViewEdit: View {
    @Binding var myObject: MyObject
    var body: some View {
        Form {
            Picker(selection: self.$myObject.enumValue, label: Text("enum values editing")) {
                ForEach(MyEnum.allCases, id: \.self) {
                    Text("\(String(describing: $0))")
                }
            }
        }
    }
}
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Saliken
  • 116
  • 8
  • i tried to mimic the same hierarchy without any trouble ... show us your model and enum. Where is you nested navigation view? From information provided, there is any – user3441734 Mar 09 '20 at 08:40
  • @user3441734 I've taken your answer and modified it to show how I'm getting the problem to occur. I'm not sure if it has something to do with EditButton and Group, as those are the main differences between my setup and yours. – Saliken Mar 09 '20 at 18:01
  • Discovered that if I use the EditButton() to show a detail or edit subview, the detail subview picker works fine, but the editview picker does not. – Saliken Mar 09 '20 at 18:06
  • I see now, what you are talking about. what do you like to edit there? Picker is disabled, just because it is not editable. what do you expect there? – user3441734 Mar 09 '20 at 19:26
  • change (add) .pickerStyle(SegmentedPickerStyle()) and you will see, that picker works, just while in editing mode, you not able to change View hierarchy. and this is standard behavior, isn't it? – user3441734 Mar 09 '20 at 19:32
  • I updated my answer with some "self explanatory" example – user3441734 Mar 09 '20 at 19:56

1 Answers1

1

We don't have an idea about your enum and model (_myObj) implementation.

In the next snippet is working code (copy - paste - test it) where you can see how to implement picker with enum. You can even uncomment the lines where Form is declared, if you like to have your Picker in Form

import SwiftUI

struct Subview: View {
    @Binding var flag: Bool
    @Binding var sel: Int
    var body: some View {
        VStack {
            Text(String(describing: MyEnum.allCases()[sel]))
            Button(action: {
                self.flag.toggle()
            }) {
                Text("toggle")
            }
            if flag {
                FlagOnView()
            } else {
                FlagOffView(sel: $sel)
            }
        }
    }
}

enum MyEnum {
    case a, b, c, d
    static func allCases()->[MyEnum] {
        [MyEnum.a, MyEnum.b, MyEnum.c, MyEnum.d]
    }
}
struct FlagOnView: View {
    var body: some View {
        Text("flag on")
    }
}
struct FlagOffView: View {
    @Binding var sel: Int
    var body: some View {
        //Form {
            Picker(selection: $sel, label: Text("select")) {
                ForEach(0 ..< MyEnum.allCases().count) { (i) in
                    Text(String(describing:  MyEnum.allCases()[i])).tag(i)
                }
            }.pickerStyle(WheelPickerStyle())
        //}
    }
}

struct ContentView: View {
    @State var sel: Int = 0
    @State var flag = false
    var body: some View {
        NavigationView {
            List {
                NavigationLink(destination: Subview(flag: $flag, sel: $sel)) {
                    Text("push to subview")
                }
                NavigationLink(destination: Text("S")) {
                    Text("S")
                }
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

UPDATE change your code

struct SubViewShow: View {
    @Binding var myObject: MyObject
    @State var sel = Set<Int>()
    var body: some View {
        Form {
            List(selection: $sel) {
                ForEach(MyEnum.allCases, id: \.self) {
                    Text("\(String(describing: $0))")
                }.onDelete { (idxs) in
                    print("delete", idxs)
                }
            }
            Picker(selection: self.$myObject.enumValue, label: Text("enum values viewing")) {
                ForEach(MyEnum.allCases, id: \.self) {
                    Text("\(String(describing: $0))")
                }
            }.pickerStyle(SegmentedPickerStyle())
        }
    }
}

struct SubViewEdit: View {
    @Binding var myObject: MyObject
    @State var sel = Set<Int>()
    var body: some View {
        Form {
            List(selection: $sel) {
                ForEach(MyEnum.allCases, id: \.self) {
                    Text("\(String(describing: $0))")
                }.onDelete { (idxs) in
                    print("delete", idxs)
                }
            }
            Picker(selection: self.$myObject.enumValue, label: Text("enum values editing")) {
                ForEach(MyEnum.allCases, id: \.self) {
                    Text("\(String(describing: $0))")
                }
            }
            .pickerStyle(SegmentedPickerStyle())
        }
    }
}

and see what happens

I think, you just misunderstood what the editing mode is for.

enter image description here

user3441734
  • 16,722
  • 2
  • 40
  • 59
  • I appreciate your continued help on this. I Know edit mode can be used for lists, however in the official apple tutorial [Working with UI Controls](https://developer.apple.com/tutorials/swiftui/working-with-ui-controls) They use the EditButton in the exact way I did my example. Scroll down to section 2 and 3 to see the examples. – Saliken Mar 09 '20 at 23:02
  • Just saw your comment on my main post. I'm unaware how edit mode effects the view hierarchy. If it's intent is to prevent navigation til editing is finished then I think I'll follow your advice and handle "edit mode" myself and use the official edit mode for lists. – Saliken Mar 09 '20 at 23:10