Post

Replies

Boosts

Views

Activity

NavigationSplitView Binding to NavigationSplitViewColumn reset with every NavigationStack path change
I try to create a Navigation that works on iPhone, iPhone Duo and iPad. NavigationSplitView collapses for compact form factors and makes iPhone Duo support much simpler. The Problem is the semantic of the collapse. Switch from detail to sidebar on compact is not a 'back' action in my case. It should have my choosen Icon and has a meaning. So i wanted to control this using the NavigationSpliViewColumn Binding. This does work forward and backward as long as no NavigationStack was used before. But it stops working when you return to the detail root from a Navigation-Action. I left the Backbutton visible so you can test, that it continues to work, when no Navigation-Action preceeded the switch. Here is my Code: import SwiftUI @Observable final class Compass { var path: [Route] init() { self.path = [] } } enum Route: Hashable { case goal } struct ContentView: View { @Environment(\.horizontalSizeClass) private var hsize @State private var compass: Compass = Compass() @State var column: NavigationSplitViewColumn = .detail var body: some View { NavigationSplitView(preferredCompactColumn: $column) { OverviewScreen(column: $column) } detail: { TodayScreen() .toolbar { ToolbarItem(placement: .topBarLeading) { Button("overview", systemImage: "calendar") { self.column = .sidebar } } } } .environment(compass) .onChange(of: column) { old, new in print("column:", old, "→", new) } } } struct OverviewScreen: View { @Binding var column: NavigationSplitViewColumn @Environment(Compass.self) var compass var body: some View { @Bindable var compass = self.compass ScrollView { Text("Overview") .font(.largeTitle) .fontWeight(.heavy) Button("Switch") { self.column = .detail } } } } struct TodayScreen: View { @Environment(Compass.self) var compass var body: some View { @Bindable var compass = self.compass NavigationStack(path: $compass.path) { ScrollView { Text("Today") .font(.largeTitle) .fontWeight(.heavy) Button("Goals programtically", systemImage: "flag.fill") { self.compass.path = [.goal] } NavigationLink(value: Route.goal) { Label("Goal Link", systemImage: "flag.fill") } } .navigationDestination(for: Route.self, destination: self.screen) } } @ContentBuilder func screen(for route: Route) -> some View { switch route { case .goal: Text("Goals") } } } #Preview { ContentView() }This seems unexpected behaviour. If you ask AI the mental model is NavigationSplitViewColumn represents which column is currently visible for the user. That model would be wrong. Also i can't understand why a NavigationStack has to change the Binding? What am i missing here? Is my idea of the programmatic switch not supported? There are other ways to push from compact to your detail column again, but they all have trade-offs or run into other problems. I would appreciate any help here. AI has not understood this and only does surface level work arounds that fix nothing.
0
0
32
2d
How do i use dynamic data for my SwiftUI ScrollView without destroying performance?
Currently i am trying really hard to create experience like the Apple fitness app. So the main view is a single day and the user can swipe between days. The week would be displayed in the toolbar and provide a shortcut to scroll to the right day. I had many attempts at solving this and it can work. You can create such an interface with SwiftUI. However, changing the data on every scroll makes limiting view updates hard and additionally the updates are not related to my code directly. Instruments show me long updates, but they belong to SwiftUI and all the advice i found does not apply or help. struct ContentView: View { @State var journey = JourneyPrototype(selection: 0) @State var position: Int? = 0 var body: some View { ScrollView(.horizontal) { LazyHStack(spacing: 0) { ForEach(journey.collection, id: \.self) { index in Listing(index: index) .id(index) } } .scrollTargetLayout() } .scrollTargetBehavior(.paging) .scrollPosition(id: $position) .onChange(of: position) { oldValue, newValue in journey.selection = newValue ?? 0 journey.update() } .onScrollPhaseChange { oldPhase, newPhase in if newPhase == .idle { journey.commit() } } } } struct Listing: View { var index: Int var body: some View { List { Section { Text("Title") .font(.largeTitle) .padding() } Section { Text("\(index)") .font(.largeTitle) .padding() } Section { Text("1 ") Text("2 ") Text("3 ") Text("4 ") Text("5 ") Text("6 ") } } .containerRelativeFrame(.horizontal) } } @Observable class JourneyPrototype { var selection: Int var collection: [Int] var nextUp: [Int]? init(selection: Int) { self.selection = selection self.collection = [selection] Task { self.collection = [-2,-1,0,1,2] } } func update() { self.nextUp = [ self.selection - 2, self.selection - 1, selection, self.selection + 1, self.selection + 2 ] } func commit() { self.collection = self.nextUp ?? self.collection self.nextUp = nil } } #Preview { ContentView() } There are some major Problem with this abstracted prototype ScrollView has no good trigger for the update, because if i update on change of the position, it will update much more than once. Thats why i had to split calculation and applying the diff The LazyHStack is not optimal, because there are only 5 View in the example, but using HStack breaks the scrollPosition Each scroll updates all List, despite changing only 2 numbers in the array. AI recommended to append and remove, which does nothing about the updates. In my actual Code i do this with Identifiable data and the Problem is the same. So the data itself is not the problem? Please consider, this is just the rough prototype to explain the problem, i am aware that an array of Ints is not ideal here, but the problem is the same in Instruments and much shorter to post. Why am i posting this? Scrolling through dynamic data is required for many apps, but there is no proper solution to this online. Github and Blogs are fine with showing a progress indicator and letting the user wait, some probably perform worse than this prototype. Other solutions require UIKit like using a UIPageViewController. But even using this i run in small hitches related to layout. Important consideration, my data for the scrollview is too big to be calculated upfront. 100 years of days that are calculated for my domain logic take too long, so i have no network request, but the need to only act on a smaller window of data. Instruments shows long update for one scroll action tested on a iPhone SE 2nd generation ListRepresentable has 7 updates and takes 17ms LazySubViewPlacements has 2 updates and takes 8ms Other long updates are too verbose to include I would be very grateful for any help.
0
0
106
Feb ’26
10162: The SwiftUI cookbook for focus - does not work as expected with iOS 18
I try to create a sheet that shows a textfield and the textfield should gain the focus immediately after the sheet is presented. This use case is explained in Session 10162 at 11:21 and at 13:31 my desired behavior is shown. I could not get this to work in my own code and downloaded the sample code from here: https://developer.apple.com/documentation/swiftui/focus-cookbook-sample Then I opened the sample code and run it in the simulator. It does not focus when the sheet appears in a iOS 18 simulator using Xcode 16 installed via the AppStore. It does gain focus if I use the "add" Button. No changes made on the sample code. Just adjusted the Team setting to get a clean build. The behavior I get locally under iOS 18 is not what is shown in the session and I can't understand why. I tried the following Simulators (and platforms) iPhone 16, iPad Pro (M4, 11inch) and my Mac. On none of those the last item got focus just by presenting the sheet. Is it not possible to test this in a simulator? Could I have a configuration error? Given all the information I found yet, this seems like a Bug. It would be very helpful if someone could replicate my problem. Thank you for your help. Programm Versions: Xcode: Version 16.0 (16A242d) MacOS: 15.0 (24A335)
0
0
644
Sep ’24
NavigationSplitView Binding to NavigationSplitViewColumn reset with every NavigationStack path change
I try to create a Navigation that works on iPhone, iPhone Duo and iPad. NavigationSplitView collapses for compact form factors and makes iPhone Duo support much simpler. The Problem is the semantic of the collapse. Switch from detail to sidebar on compact is not a 'back' action in my case. It should have my choosen Icon and has a meaning. So i wanted to control this using the NavigationSpliViewColumn Binding. This does work forward and backward as long as no NavigationStack was used before. But it stops working when you return to the detail root from a Navigation-Action. I left the Backbutton visible so you can test, that it continues to work, when no Navigation-Action preceeded the switch. Here is my Code: import SwiftUI @Observable final class Compass { var path: [Route] init() { self.path = [] } } enum Route: Hashable { case goal } struct ContentView: View { @Environment(\.horizontalSizeClass) private var hsize @State private var compass: Compass = Compass() @State var column: NavigationSplitViewColumn = .detail var body: some View { NavigationSplitView(preferredCompactColumn: $column) { OverviewScreen(column: $column) } detail: { TodayScreen() .toolbar { ToolbarItem(placement: .topBarLeading) { Button("overview", systemImage: "calendar") { self.column = .sidebar } } } } .environment(compass) .onChange(of: column) { old, new in print("column:", old, "→", new) } } } struct OverviewScreen: View { @Binding var column: NavigationSplitViewColumn @Environment(Compass.self) var compass var body: some View { @Bindable var compass = self.compass ScrollView { Text("Overview") .font(.largeTitle) .fontWeight(.heavy) Button("Switch") { self.column = .detail } } } } struct TodayScreen: View { @Environment(Compass.self) var compass var body: some View { @Bindable var compass = self.compass NavigationStack(path: $compass.path) { ScrollView { Text("Today") .font(.largeTitle) .fontWeight(.heavy) Button("Goals programtically", systemImage: "flag.fill") { self.compass.path = [.goal] } NavigationLink(value: Route.goal) { Label("Goal Link", systemImage: "flag.fill") } } .navigationDestination(for: Route.self, destination: self.screen) } } @ContentBuilder func screen(for route: Route) -> some View { switch route { case .goal: Text("Goals") } } } #Preview { ContentView() }This seems unexpected behaviour. If you ask AI the mental model is NavigationSplitViewColumn represents which column is currently visible for the user. That model would be wrong. Also i can't understand why a NavigationStack has to change the Binding? What am i missing here? Is my idea of the programmatic switch not supported? There are other ways to push from compact to your detail column again, but they all have trade-offs or run into other problems. I would appreciate any help here. AI has not understood this and only does surface level work arounds that fix nothing.
Replies
0
Boosts
0
Views
32
Activity
2d
How do i use dynamic data for my SwiftUI ScrollView without destroying performance?
Currently i am trying really hard to create experience like the Apple fitness app. So the main view is a single day and the user can swipe between days. The week would be displayed in the toolbar and provide a shortcut to scroll to the right day. I had many attempts at solving this and it can work. You can create such an interface with SwiftUI. However, changing the data on every scroll makes limiting view updates hard and additionally the updates are not related to my code directly. Instruments show me long updates, but they belong to SwiftUI and all the advice i found does not apply or help. struct ContentView: View { @State var journey = JourneyPrototype(selection: 0) @State var position: Int? = 0 var body: some View { ScrollView(.horizontal) { LazyHStack(spacing: 0) { ForEach(journey.collection, id: \.self) { index in Listing(index: index) .id(index) } } .scrollTargetLayout() } .scrollTargetBehavior(.paging) .scrollPosition(id: $position) .onChange(of: position) { oldValue, newValue in journey.selection = newValue ?? 0 journey.update() } .onScrollPhaseChange { oldPhase, newPhase in if newPhase == .idle { journey.commit() } } } } struct Listing: View { var index: Int var body: some View { List { Section { Text("Title") .font(.largeTitle) .padding() } Section { Text("\(index)") .font(.largeTitle) .padding() } Section { Text("1 ") Text("2 ") Text("3 ") Text("4 ") Text("5 ") Text("6 ") } } .containerRelativeFrame(.horizontal) } } @Observable class JourneyPrototype { var selection: Int var collection: [Int] var nextUp: [Int]? init(selection: Int) { self.selection = selection self.collection = [selection] Task { self.collection = [-2,-1,0,1,2] } } func update() { self.nextUp = [ self.selection - 2, self.selection - 1, selection, self.selection + 1, self.selection + 2 ] } func commit() { self.collection = self.nextUp ?? self.collection self.nextUp = nil } } #Preview { ContentView() } There are some major Problem with this abstracted prototype ScrollView has no good trigger for the update, because if i update on change of the position, it will update much more than once. Thats why i had to split calculation and applying the diff The LazyHStack is not optimal, because there are only 5 View in the example, but using HStack breaks the scrollPosition Each scroll updates all List, despite changing only 2 numbers in the array. AI recommended to append and remove, which does nothing about the updates. In my actual Code i do this with Identifiable data and the Problem is the same. So the data itself is not the problem? Please consider, this is just the rough prototype to explain the problem, i am aware that an array of Ints is not ideal here, but the problem is the same in Instruments and much shorter to post. Why am i posting this? Scrolling through dynamic data is required for many apps, but there is no proper solution to this online. Github and Blogs are fine with showing a progress indicator and letting the user wait, some probably perform worse than this prototype. Other solutions require UIKit like using a UIPageViewController. But even using this i run in small hitches related to layout. Important consideration, my data for the scrollview is too big to be calculated upfront. 100 years of days that are calculated for my domain logic take too long, so i have no network request, but the need to only act on a smaller window of data. Instruments shows long update for one scroll action tested on a iPhone SE 2nd generation ListRepresentable has 7 updates and takes 17ms LazySubViewPlacements has 2 updates and takes 8ms Other long updates are too verbose to include I would be very grateful for any help.
Replies
0
Boosts
0
Views
106
Activity
Feb ’26
10162: The SwiftUI cookbook for focus - does not work as expected with iOS 18
I try to create a sheet that shows a textfield and the textfield should gain the focus immediately after the sheet is presented. This use case is explained in Session 10162 at 11:21 and at 13:31 my desired behavior is shown. I could not get this to work in my own code and downloaded the sample code from here: https://developer.apple.com/documentation/swiftui/focus-cookbook-sample Then I opened the sample code and run it in the simulator. It does not focus when the sheet appears in a iOS 18 simulator using Xcode 16 installed via the AppStore. It does gain focus if I use the "add" Button. No changes made on the sample code. Just adjusted the Team setting to get a clean build. The behavior I get locally under iOS 18 is not what is shown in the session and I can't understand why. I tried the following Simulators (and platforms) iPhone 16, iPad Pro (M4, 11inch) and my Mac. On none of those the last item got focus just by presenting the sheet. Is it not possible to test this in a simulator? Could I have a configuration error? Given all the information I found yet, this seems like a Bug. It would be very helpful if someone could replicate my problem. Thank you for your help. Programm Versions: Xcode: Version 16.0 (16A242d) MacOS: 15.0 (24A335)
Replies
0
Boosts
0
Views
644
Activity
Sep ’24