Why do push animations break after switching list data in NavigationSplitView sidebar?

99 views Asked by At

I have a complex-ish sidebar in my NavigationSplitView where I'm letting the user switch between two kinds of list data using a picker in the toolbar. Unfortunately, I've run into an issue where the NavigationSplitView won't show the pushing animation the first time the user selects a list item after picking the list data. The code works fine on iPad but breaks specifically on iPhone.

I did find this bug on the Apple forums, but it doesn't pertain to me because I'm using a NavigationSplitView whereas the bug involves NavigationStack.

Here is a gif of my issue.

Here is my code:

import SwiftUI

struct ContentView: View {
  @State private var tab: PickerTab = .fruits
  @State private var selectedFruit: Fruit?
  @State private var selectedVegetable: Vegetable?
  
  private var fruits: [Fruit] = [
    Fruit(name: "Strawberry", color: .red),
    Fruit(name: "Blueberry", color: .blue)
  ]
  
  private var vegetables: [Vegetable] = [
    Vegetable(name: "Carrot", color: .orange),
    Vegetable(name: "Broccoli", color: .green)
  ]
  
  var body: some View {
    NavigationSplitView {
      Group {
        switch tab {
        case .fruits:
          List(fruits, selection: $selectedFruit) { fruit in
            NavigationLink(value: fruit) {
              Text(fruit.name)
            }
          }
        case .vegetables:
          List(vegetables, selection: $selectedVegetable) { vegetable in
            NavigationLink(value: vegetable) {
              Text(vegetable.name)
            }
          }
        }
      }
      .toolbar {
        ToolbarItem(placement: .principal) {
          Picker("Sidebar", selection: $tab) {
            ForEach(PickerTab.allCases, id: \.self) { tab in
              Text(tab.rawValue.capitalized)
            }
          }
          .pickerStyle(.segmented)
        }
      }
    } detail: {
      switch tab {
      case .fruits:
        if let selectedFruit {
          Text(selectedFruit.name)
            .foregroundStyle(selectedFruit.color)
        }
      case .vegetables:
        if let selectedVegetable {
          Text(selectedVegetable.name)
            .foregroundStyle(selectedVegetable.color)
        }
      }
    }
  }
}

enum PickerTab: String, Hashable, CaseIterable {
  case fruits
  case vegetables
}

struct Fruit: Hashable, Identifiable {
  let id = UUID()
  let name: String
  let color: Color
}

struct Vegetable: Hashable, Identifiable {
  let id = UUID()
  let name: String
  let color: Color
}
1

There are 1 answers

0
ChrisR On

I can confirm the behaviour and it seems a bug to me.

It looks like it has to do with the use of selection: in List... Here is a version without selection, making use of .navigationDestination. If this is an option for you it might be a workaround.

    var body: some View {
        NavigationSplitView {
            Group {
                switch tab {
                case .fruits:
                    List(fruits) { fruit in
                        NavigationLink(value: fruit) {
                            Text(fruit.name)
                        }
                    }
                case .vegetables:
                    List(vegetables) { vegetable in
                        NavigationLink(value: vegetable) {
                            Text(vegetable.name)
                        }
                    }
                }
            }
            .navigationDestination(for: Fruit.self) { fruit in
                Text(fruit.name)
                    .foregroundStyle(fruit.color)
            }
            .navigationDestination(for: Vegetable.self) { vegetable in
                Text(vegetable.name)
                    .foregroundStyle(vegetable.color)
            }

            .toolbar {
                ToolbarItem(placement: .principal) {
                    Picker("Sidebar", selection: $tab) {
                        ForEach(PickerTab.allCases, id: \.self) { tab in
                            Text(tab.rawValue.capitalized)
                        }
                    }
                    .pickerStyle(.segmented)
                }
            }

        } detail: {
            // nothing needed here, taken care of the .navigationDestination
        }

    }