let stringLimits = string.startIndex ..< string.endIndex
let clampedRange = range.clamped(to: stringLimits)
let substring = string[clampedRange]
Same crash.
Is there a function like:
Bool ok = string(compatibleWith: range)
Or something to this effect?
The reason seems to be that my range is not relative to my string.
On macOS:
@State var texSelection: TextSelection? = nil
...
Text("Click here")
.textSelection(.enabled)
.onTapGesture // does NOT work on macOS
{
print("onTapGesture in Text")
}
TextField("Enter Text", text: $string, selection: $texSelection)
Clicking in the TextField sets texSelection; so far ok.
But clicking in Text also sets texSelection with a range relative to "Click here". This then will result further down in: "Fatal error: String index is out of bounds"
This interaction between textSelection() and a @State ... : TextSelection where all selected text in the View uses the same State variable is, on first usage, jarring to say the least. This is largely a SwiftUI framework quirk.
However, to help with the immediate issue, you can use FocusState to determine whether the TextField or any Text_ views are being focused and only get the substring from that relevant text source.
Given your little snippet at the end, here is a sample that will get the relevant selected text either the Text or TextField.
struct ContentView: View {
@State
var textSelection: TextSelection? = nil
@State
var string: String = "asdf"
@State
var textText: String = "Click here"
@FocusState
private var isTextFieldFocused: Bool
var body: some View {
VStack {
Text(textText)
.textSelection(.enabled)
TextField(
"Enter Text",
text: $string,
selection: $textSelection
)
.focused($isTextFieldFocused)
}
.onChange(of: textSelection) { _, newValue in
guard let newValue else { return }
switch newValue.indices {
case .selection(let range):
if isTextFieldFocused {
print("TextField selection: \(string[range])")
} else {
print("Text selection: \(textText[range])")
}
case .multiSelection(let rangeSet):
if isTextFieldFocused {
print("TextField selection: \(string[rangeSet])")
} else {
print("Text selection: \(textText[rangeSet])")
}
}
}
}
}
#Preview {
ContentView()
}