Search code examples
swiftuifocusswiftui-navigationlink

SwiftUI: NavigationView focus on last selected NavigationLink


maybe a very simple problem: I use a navigation with a long list of entries. If the user returns from the navigationLink the list starts on the first item. How can I set the focus on the last selected navigationLink so the user don't need to scroll from the beginning again. My app is for blind people so the scrolling from above isn't an easy thing.

´´´ struct CategoryDetailView: View {

@EnvironmentObject var blindzeln: BLINDzeln
@AppStorage ("version") var version: Int = 0
@State var shouldRefresh: Bool = false
@State private var searchText = ""
let categoryTitle: String
let catID: Int

var body: some View {
    VStack{
        List {
            ForEach(blindzeln.results.filter { searchText.isEmpty || ($0.title.localizedCaseInsensitiveContains(searchText) || $0.textBody.localizedCaseInsensitiveContains(searchText)) }, id: \.entryID){ item in
           
                    NavigationLink(destination: ItemDetailViewStandard(item: item, isFavorite: false, catID: catID)) {DisplayEntryView(item: item, catID: catID)}.listRowSeparatorTint(.primary).listRowSeparator(.hidden) 
            }
        }
        .searchable(text: $searchText, placement: .navigationBarDrawer(displayMode: .always), prompt: "") {}
        
        .navigationTitle(categoryTitle)

        .navigationBarTitleDisplayMode(.inline)
        .listStyle(.inset)
    }
    .task(){
        await blindzeln.decodeCategoryData(showCategory: categoryTitle)
    }
    .onAppear(){
    blindzeln.resetData()
    }
}

} ´´´


Solution

  • you could try this approach, using the List with selection, such as in this example code. It does not scroll back to the beginning of the list after selecting a destination.

    struct ContentView: View {
        @State private var selections = Set<Thing>()
        @State var things: [Thing] = []
        
        var body: some View {
            NavigationStack {
                List(things, selection: $selections){ thing in
                    NavigationLink(destination: Text("destination-\(thing.val)")) {
                        Text("item-\(thing.val)")
                    }
                }
            }
            .onAppear {
                (0..<111).forEach{things.append(Thing(val: $0))}
            }
        }
    }
    

    EDIT-1:

    Since there are so many elements missing from you code, I can only guess and suggest something like this:

    struct CategoryDetailView: View {
        
        @EnvironmentObject var blindzeln: BLINDzeln
        @AppStorage ("version") var version: Int = 0
        
        @State var shouldRefresh: Bool = false
        @State private var searchText = ""
        
        @State private var selections = Set<Thing>() // <-- same type as item in the List
        
        let categoryTitle: String
        let catID: Int
        
        var body: some View {
            VStack {
                // -- here
                List(blindzeln.results.filter { searchText.isEmpty || ($0.title.localizedCaseInsensitiveContains(searchText) || $0.textBody.localizedCaseInsensitiveContains(searchText)) },
                     id: \.entryID,
                     selection: $selections){ item in
                    
                    NavigationLink(destination: ItemDetailViewStandard(item: item, isFavorite: false, catID: catID)) {
                        DisplayEntryView(item: item, catID: catID)
                    }
                    .listRowSeparatorTint(.primary).listRowSeparator(.hidden)
                }
            }
            .searchable(text: $searchText, placement: .navigationBarDrawer(displayMode: .always), prompt: "") {}
            
            .navigationTitle(categoryTitle)
            .navigationBarTitleDisplayMode(.inline)
            .listStyle(.inset)
            .task{
                await blindzeln.decodeCategoryData(showCategory: categoryTitle)
            }
            .onAppear{
                blindzeln.resetData()
            }
        }
        
    }