Post

Replies

Boosts

Views

Activity

SwiftUI NavigationSplitView - Nested List Not Animating
Hi All! I've encountered a SwiftUI NavigationSplitView bug in my app that I'd appreciate help with, I've spent quite some time trying to work out a solution to this problem. I have a main list, which has either a Location or LocationGroup. When selecting a Location, the detail view is presented. When selecting a LocationGroup, a subsequent list of Locations are presented (from the group). When tapping on any of these Locations, the NavigationStack does not animate (push the view), and when tapping back from it, it jumps to the main selection. The overall goal is to be able to present within the detail: of a NavigationSplitView a NavigationStack, and to be able to push onto that stack from both the main NavigationSplitView's list, and from the detail. I created the following sample code that reproduces the issue: ContentView.swift public enum LocationSplitViewNavigationItem: Identifiable, Hashable { case location(Location) case locationGroup(LocationGroup) public var id: UUID? { switch self { case .location(let location): return location.id case .locationGroup(let locationGroup): return locationGroup.id } } } struct ContentView: View { @State private var columnVisibility = NavigationSplitViewVisibility.doubleColumn @State private var selectedNavigationItem: LocationSplitViewNavigationItem? var body: some View { NavigationSplitView(columnVisibility: $columnVisibility) { LocationListView(selectedItem: $selectedNavigationItem, locationData: LocationSampleData(locations: LocationSampleData.sampleLocations, locationGroups: LocationSampleData.sampleLocationGroups)) } detail: { if let selectedLocation = selectedNavigationItem { switch selectedLocation { case .location(let location): LocationDetailView(selectedLocation: location) case .locationGroup(let locationGroup): LocationListView(selectedItem: $selectedNavigationItem, locationData: LocationSampleData(locations: locationGroup.locations)) } } } .navigationSplitViewStyle(.balanced) } } LocationListView.swift struct LocationListView: View { @Binding public var selectedItem: LocationSplitViewNavigationItem? var locationData: LocationSampleData var body: some View { List(selection: $selectedItem) { if let locations = locationData.locations { ForEach(locations) { location in NavigationLink(value: LocationSplitViewNavigationItem.location(location)) { Text(location.name) .bold() } } } if let locationGroups = locationData.locationGroups { ForEach(locationGroups) { locationGroup in NavigationLink(value: LocationSplitViewNavigationItem.locationGroup(locationGroup)) { Text(locationGroup.name) .bold() .foregroundStyle(.red) } } } } .navigationTitle("Saturday Spots") .navigationBarTitleDisplayMode(.large) } } LocationDetailView.swift struct LocationDetailView: View { var selectedLocation: Location var body: some View { Text(selectedLocation.name) .font(.largeTitle) .bold() .foregroundStyle(LinearGradient( colors: [.teal, .indigo], startPoint: .top, endPoint: .bottom )) .toolbarBackground( Color.orange, for: .navigationBar ) .toolbarBackground(.visible, for: .navigationBar) } } Location.swift import CoreLocation struct LocationSampleData { var locations: [Location]? var locationGroups: [LocationGroup]? static let sampleLocations: [Location]? = Location.sample static let sampleLocationGroups: [LocationGroup]? = [LocationGroup.sample] } public struct Location: Hashable, Identifiable { var name: String var coordinates: CLLocationCoordinate2D var photo: String public var id = UUID() static public let sample: [Location] = [ Location(name: "Best Bagel & Coffee", coordinates: CLLocationCoordinate2D(latitude: 123, longitude: 121), photo: "asdf"), Location(name: "Absolute Bagels", coordinates: CLLocationCoordinate2D(latitude: 123, longitude: 121), photo: "asdf"), Location(name: "Tompkins Square Bagels", coordinates: CLLocationCoordinate2D(latitude: 123, longitude: 121), photo: "asdf"), Location(name: "Zabar's", coordinates: CLLocationCoordinate2D(latitude: 123, longitude: 121), photo: "asdf"), ] static public let oneSample = Location(name: "Absolute Bagels", coordinates: CLLocationCoordinate2D(latitude: 123, longitude: 121), photo: "asdf") } public struct LocationGroup: Identifiable, Hashable { var name: String var locations: [Location] public var id = UUID() static public let sample: LocationGroup = LocationGroup(name: "Bowling", locations: [ Location(name: "Frames Bowling Lounge", coordinates: CLLocationCoordinate2D(latitude: 123, longitude: 121), photo: "asdf"), Location(name: "Bowlero Chelsea Piers", coordinates: CLLocationCoordinate2D(latitude: 123, longitude: 121), photo: "asdf") ]) } extension CLLocationCoordinate2D: Hashable { public static func == (lhs: Self, rhs: Self) -> Bool { return lhs.latitude == rhs.latitude && lhs.longitude == rhs.longitude } public func hash(into hasher: inout Hasher) { hasher.combine(latitude) hasher.combine(longitude) } }
2
0
710
Aug ’24
iOS 18 - Toolbar keyboard placement within TabView, NavigationSplitView
Hello, After a fair amount of time of trial and error, I have seemingly discovered a bug in iOS 18 with .toolBar with placement:. keyboard) Feedback: FB15205988 Xcode Version: 16.0 (16A242d) iOS Version: 18.0 (22A3351) I'd appreciate help to see if I'm missing something here. What's happening? When using .toolBar with (placement: .keyboard), within a NavigationSplitView detail:, encompassed in a TabView, the .toolBar view does not appear above the keyboard. Within the code, the usage of .toolBar is within ShakeDetail.swift. When commenting out the TabView within ShakeTabView.swift, or running on iOS 17.5, the .toolbar appears. Would greatly appreciate help if someone knows a workaround, or a misunderstanding here of course! Code Shake.swift import Foundation import SwiftUI struct Shake: Equatable, Identifiable, Hashable { var name: String let id = UUID() static var example = Shake(name: "Banana") static var many: [Shake] { return [ Shake(name: "Apple"), Shake(name: "Banana"), Shake(name: "Cherry"), Shake(name: "Berry"), ] } } ShakeDetail.swift import SwiftUI struct ShakeDetail: View { @State var shake: Shake = Shake(name: "") var body: some View { List { TextField("dsf", text: $shake.name, prompt: Text("Enter your favorite shake")) } .scrollDismissesKeyboard(.interactively) .toolbar { ToolbarItem(placement: .keyboard) { HStack(alignment: .center) { Text(shake.name) .font(.title3) .foregroundColor(.red) } } } .toolbarColorScheme(.dark, for: .navigationBar) .toolbarBackground( Color.purple, for: .navigationBar ) .toolbarBackground(.visible, for: .navigationBar) } } #Preview { ShakeDetail() } ShakeListView.swift struct ShakeListView: View { @Binding var shakes: [Shake] @Binding var selectedShake: Shake? var body: some View { List(selection: $selectedShake) { ForEach(shakes) { shake in NavigationLink(value: shake) { Text(shake.name) .font(.system(.title2, design: .rounded)) .foregroundStyle(.primary) } } } .navigationTitle("Shakes") } } #Preview { @Previewable @State var shakes: [Shake] = Shake.many @Previewable @State var selectedShake: Shake? ZStack { ShakeListView(shakes: $shakes, selectedShake: $selectedShake) } } ShakeSplitView.swift import SwiftUI import OSLog public struct ShakeSplitView: View { @State var selectedShake: Shake? @State var shakes = Shake.many @State private var columnVisibility = NavigationSplitViewVisibility.doubleColumn public init(isShowingFavorites: Bool = false, columnVisibility: SwiftUI.NavigationSplitViewVisibility = NavigationSplitViewVisibility.doubleColumn) { self.columnVisibility = columnVisibility } public var body: some View { NavigationSplitView(columnVisibility: $columnVisibility) { ShakeListView(shakes: $shakes, selectedShake: $selectedShake) } detail: { if let selectedShake { // Add shake detail ShakeDetail(shake: selectedShake) } else { Label("Please select a shake", systemImage: "arrow.backward.circle") .foregroundColor(.purple) .font(.system(.title2, design: .rounded)) } } .navigationSplitViewStyle(.balanced) } } struct FormulaSplitView_Previews: PreviewProvider { static var previews: some View { ShakeSplitView() } } ShakeTabView.swift import SwiftUI struct ShakeTabView: View { var body: some View { /* TabView breaks the ToolbarItem(placement: .keyboard) */ TabView { ShakeSplitView() .tabItem { Image(systemName: "carrot.fill") Text("Shakes") } } } } #Preview { ShakeTabView() }
4
0
569
Sep ’24