Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to stop text cursor from jumping to the end in SwiftUI?

Tags:

swiftui

I'm curious, has anyone seen this before, or do they know how to solve it? I have a situation where editing a textfield that's in a NavigationStack always pops the text cursor to the end of the field on every keystroke. I suspect it has something to do with SwiftUI's management of views and state, but I am not spotting anything unusual that I might be doing, other than the index lookup in the navigationDestination part. I don't understand why that would be a problem.

Here's some pretty minimal code demonstrating the problem (just try correcting the well-known Shakespeare quote):

struct CursorResetting: View {
    struct Record: Identifiable {
        var string = ""
        var id = UUID()
    }

    @State var path = NavigationPath()
    @State private var records = [
        Record(string: "To be and not to be"),
        Record(string: "That begs the question")
    ]

    var body: some View {
        NavigationStack(path: $path) {
            Form {
                List {
                    ForEach(records) { record in
                        NavigationLink(value: record.id) {
                            Text(record.string)
                        }
                    }
               }
            }
            .navigationDestination(for: UUID.self) { id in
                let index = records.firstIndex { $0.id == id }
                if let index {
                    Form {
                        TextField("Value", text: $records[index].string)
                    }
                } else {
                    Text("Invalid ID")
                }
            }
        }
    }
}
like image 616
Curious Jorge Avatar asked Oct 17 '25 15:10

Curious Jorge


1 Answers

This is a known thing, and the reason why you can't use a Binding type with navigationDestination.

Binding triggers the View to redraw and it resets everything in the body including the navigationDestination modifier.

You have to use NavigationLink(destination:, label:)

import SwiftUI

struct CursorResettingView: View {
    struct Record: Identifiable {
        var string = ""
        var id = UUID()
    }
    
    @State var path = NavigationPath()
    @State private var records = [
        Record(string: "To be and not to be"),
        Record(string: "That begs the question")
    ]
    
    var body: some View {
        NavigationStack(path: $path) {
            Form {
                List {
                    ForEach($records) { $record in
                        NavigationLink {
                            Form {
                                TextField("Value", text: $record.string)
                            }
                        } label: {
                            Text(record.string)
                        }

                    }
                }
            }
            .navigationDestination(for: UUID.self) { id in
                //Use this for `View`s that don't use `Binding` type as argument.
            }
        }
    }
}
struct CursorResettingView_Previews: PreviewProvider {
    static var previews: some View {
        CursorResettingView()
    }
}

Anything that uses an array's index is generally considered unsafe with SwiftUI so "solutions" that depend on it will be inherently fragile.

like image 177
lorem ipsum Avatar answered Oct 21 '25 11:10

lorem ipsum



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!