My Problem: I want the user to be able to go from Textfield to TextField without the view bouncing as shown in the gif below.
My Use Case: I have multiple TextFields and TextEditors in multiple child views. These TextFields are generated dynamically so I want the FocusState to be a separate concern.
I made an example gif and code sample below.
Please check it out, any suggestions appreciated.
As suggested in the comments I made some changes with no effect to the bounce:
- Using Identfiable does not change the bounce
- A single observed object or multiple and a view model does not change the bounce
I think this is from the state change refresh. If it's not the refresh causing the bounce(as the user suggests in the comments) what is? Is there a way to stop this bounce while using FocusState?
To Reproduce: Create a new iOS app xcode project and replace the content view with this code body below. It seems to refresh the view when the user goes from one textfield to the next textfield causing a bounce of the whole screen.
Code Example
import SwiftUI
struct MyObject: Identifiable, Equatable {
var id: String
public var value: String
init(name: String, value: String) {
self.id = name
self.value = value
}
}
struct ContentView: View {
@State var myObjects: [MyObject] = [
MyObject(name: "aa", value: "1"),
MyObject(name: "bb", value: "2"),
MyObject(name: "cc", value: "3"),
MyObject(name: "dd", value: "4")
]
@State var focus: MyObject?
var body: some View {
VStack {
Text("Header")
ForEach(self.myObjects) { obj in
Divider()
FocusField(displayObject: obj, focus: $focus, nextFocus: {
guard let index = self.myObjects.firstIndex(of: $0) else {
return
}
self.focus = myObjects.indices.contains(index + 1) ? myObjects[index + 1] : nil
})
}
Divider()
Text("Footer")
}
}
}
struct FocusField: View {
@State var displayObject: MyObject
@FocusState var isFocused: Bool
@Binding var focus: MyObject?
var nextFocus: (MyObject) -> Void
var body: some View {
TextField("Test", text: $displayObject.value)
.onChange(of: focus, perform: { newValue in
self.isFocused = newValue == displayObject
})
.focused(self.$isFocused)
.submitLabel(.next)
.onSubmit {
self.nextFocus(displayObject)
}
}
}
MyObject
andMyObjViewModel
both observable and why are the both observed inFocusField
? I am not sure you need to observeMyObject
, so shouldn't it just be a struct and be identifiable? Why do you have a.submitLabel
if you are not using it? The fact of a refresh should not be causing the bounce that occurs with this code. There is more to that issue. The simplest way to implement this would be to keep the focus code inContenView
and pull it out of the model. – Tion==
comparison, perhaps Identifiable would work too. I pulled the focus code into the content view and the problem persists, I posted a new example with that update in an edit of the original question according to your suggestions. Also, I am using the .submitLabel, what do you mean by that comment? What does cause the bounce then if not a refresh from state change? Please try the code out to see – CuisseMyObject
as a class & didn't put in anid:
. It is not syntactic sugar in astruct
with anid:
. Does your MRE now accurately reflect the view hierarchy in your app? You mentioned aTextArea
which would be aTextEditor
in this context, but I do not see one listed. That probably does not matter, but you mentioned that specifically and said "which makes certain things not work." – Tion