SwiftUI Picker crashes when displayed via NavigationLink
Asked Answered
D

2

5

Does anyone have a workaround for the following crash?

I have a form that is displayed via NavigationLink in a parent Navigation Controller like so:

    var body: some View {
        NavigationView {
            NavigationLink(destination: PickerView()) {
                Text("Picker View")
            }
        }
    }

The PickerView has three pickers. The first determines which of the others is shown:

struct PickerView: View {
    @State var sectionValue = "pet"
    @State var petValue = "dog"
    @State var fruitValue = "apple"
    @State var foodValue = "pasta"
    var body: some View {

        Form {

            Picker(selection: $sectionValue, label: Text("What is your favorite?")) {
                Text("Pet").tag("pet")
                Text("Fruits").tag("fruits")
                Text("Foods").tag("foods")
            }

            if (sectionValue == "pet") {
                Picker(selection: $petValue, label: Text("Favorite pet")) {
                    Text("Dog").tag("dog")
                    Text("Cat").tag("cat")
                    Text("Lizard").tag("lizard")
                }
            } else if (sectionValue == "fruits") {
                Picker(selection: $fruitValue, label: Text("Favorite fruit")) {
                    Text("Apple").tag("apple")
                    Text("Pear").tag("pear")
                    Text("Orange").tag("orange")
                }
            } else if (sectionValue == "foods") {
                Picker(selection: $foodValue, label: Text("Favorite food")) {
                    Text("Pasta").tag("pasta")
                    Text("Ice Cream").tag("ice_cream")
                    Text("Bacon").tag("bacon")
                }
            }
        }
    }
}

In the iOS 13.3 simulator (and device), I see the following behavior: Navigating to the PickerView and selecting the alternate value for the first picker will hide the 2nd picker and show the 3rd picker, as expected. However if you operate the 3rd picker, it will show with blank values... followed shortly by a crash.

The crash shows a stack trace with hundreds of calls to [UINavigationController _navigationBar:itemEnabledAutoScrollTransition:]

I think this is an Apple bug. I have filed FB7534235 but I wanted to see if anyone has any workarounds or suggestions?

One option is to use .disabled() to disable (rather than hide) the picker, but this results in a more confusing user interface.

Side note: This appears to be an interaction with NavgiationView()/NagivationLink() and Picker() -- because if you comment out the NagivationLink and render the PickerView directly in the NavigationView, everything works as expected without any crashing.

update: Sample case updated to make it a 3-way choice for the sub-pickers.. Thanks to @krjw below pointing out that in the two-way case, an "else if" rather than two separate if-statements can yield the desired behavior without a crash... though I'm still not sure why (unless it is, "just a bug")

Doctrinaire answered 14/1, 2020 at 12:10 Comment(3)
You named the workaround - put it in own view hierarchy, ex. sheet, with own NavigationView.Servitor
I'm getting a similar issue but not with a PickerView. But I, too, have hundreds of calls to that method. Do you have any more information on why this is happening? Or any feedback on your bug report?Tithonus
I'm seeing it as well. In my case, I've made a "picker" alternative that works the way I want it. Within that "picker", I'm using presentationMode.wrappedValue.dismiss(). Most of the time it works as expected. But I've run into a case involving how I delete characters from a TextField that can cause the error you mention. I'm not sure why a TextField and my "picker" have this strange interaction. If I remove the dismiss() call, it all works as expected.Desiccated
S
3

I tested this with Xcode 11.3.1 on an iPad Pro running iPadOS 13.3. I got a strange reloading behaviour when choosing something in the first picker and then choosing again, but no crash.

I could replicate the crash on my iPhone.

Adding an else on the second if clause fixes that!

So the solution is the following code:

struct PickerView: View {
    @State var sectionValue = "phonetic"
    @State var phoneticValue = "alpha"
    @State var fruitValue = "apple"

    var body: some View {

        Form {

            Picker(selection: $sectionValue, label: Text("Pick a Section")) {
                Text("Phonetic Alphabet").tag("phonetic")
                Text("Fruits").tag("fruits")
            }

            if (sectionValue == "phonetic") {
                Picker(selection: $phoneticValue, label: Text("Pick a letter")) {
                    Text("Alpha").tag("alpha")
                    Text("Bravo").tag("bravo")
                    Text("Charlie").tag("charlie")
                }
            }
            else if (sectionValue == "fruits") {
                Picker(selection: $fruitValue, label: Text("Pick a fruit")) {
                    Text("Apple").tag("apple")
                    Text("Pear").tag("pear")
                    Text("Orange").tag("orange")
                }
            }
        }
    }
}

struct ContentView: View {

    var body: some View {
        NavigationView {
            NavigationLink(destination: PickerView()) {
                Text("Picker View")
            }
        }
    }
}

I hope this helps!

Sprag answered 14/1, 2020 at 15:15 Comment(3)
This is interesting, because the 'else' does actually change the behavior in the case of a two-way toggle. Sadly, I over simplified by test case case, and in my real code, I have a 3-way Picker that chooses one of 3 possible sub-pickers... and in this case, I'm finding the else statement still doesn't help the final picker.... and the same crash is observed.Doctrinaire
13.3.1 iOS putting else if doens't help. I have a picker and a text under ifElmer
I was having a similar issue. Turns out I was setting the ID of each picker to some value that the picker changed (for example, Picker(selection: $myVariable, label: Text("Broken")){...}.id(myVariable). Changing the ID to a constant fixed itFrowst
E
3

I stuck upon the same issue and putting "else if" didn't help. What I realized is that crash appears when I have navigationBarItems somewhere outside of the view where Picker appears.

Therefore as a workaround I moved Picker into sheet() and put navigationBarItems on the same View.

It's a shame they have such crazy errors.

Elmer answered 20/1, 2020 at 9:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.