TextField in SwiftUI loses focus when I enter a character
Your code has some fundamental errors. Please research about ObservableObject
and Published
values before going into production with this code. Otherwise, it would be quite hard to deal with this code later.
I have updated your views and that seems to work. You are not using ObservableObject
as they should be used. Just pass ObservableObjects
s around let them do the bindings for you instead of setting custom bindings.
struct ExerciseView: View {
@ObservedObject var presenter: WorkoutPresenter
@ObservedObject var exercise: WorkoutExerciseVM
var body: some View {
VStack {
VStack(alignment: .leading) {
ForEach(exercise.sets, id: \.self) { exerciseSet in
ExerciseSetView(set: exerciseSet)
}
}
HStack {
Button(action: {
self.presenter.addSet(id: self.exercise.id)
}) {
HStack {
Image(systemName: "plus")
Text("Add Set")
}
}
Button(action: {
self.presenter.removeSet(id: self.exercise.id)
}) {
HStack {
Image(systemName: "minus")
Text("Remove Set")
}
}
}
}
}
}
struct ExerciseSetView: View {
@ObservedObject var set: WorkoutExerciseSetVM
var body: some View {
HStack {
Spacer()
TextField("", text: $set.reps)
Spacer()
TextField("", text: $set.weight)
}
}
}
Let me know if this works for you.
How can I stop a SwiftUI TextField from losing focus when binding within a ForEach loop?
try this:
ForEach(userPhonesManager.allPhones.indices, id: \.self) { index in
HStack {
Button(action: {
userPhonesManager.deletePhoneNumber(at: index)
}) {
Image(systemName: "minus.circle.fill")
}.buttonStyle(BorderlessButtonStyle())
TextField("Phone", text: $userPhonesManager.allPhones[index].phoneNumber)
}
}
EDIT-1:
Reviewing my comment and in light of renewed interest, here is a version without using indices
.
It uses the ForEach
with binding feature of SwiftUI 3 for ios 15+:
class PhoneDetailsStore: ObservableObject {
@Published var allPhones: [PhoneDetails]
init(phones: [PhoneDetails]) {
allPhones = phones
}
func addNewPhoneNumber() {
allPhones.append(PhoneDetails(phoneNumber: "", phoneType: "cell"))
}
// -- here --
func deletePhoneNumber(of phone: PhoneDetails) {
allPhones.removeAll(where: { $0.id == phone.id })
}
}
struct PhoneDetails: Identifiable, Equatable, Hashable {
let id = UUID() // <--- here
var phoneNumber: String
var phoneType: String
}
struct ContentView: View {
@ObservedObject var userPhonesManager: PhoneDetailsStore = PhoneDetailsStore(
phones: [
PhoneDetails(phoneNumber: "800–692–7753", phoneType: "cell"),
PhoneDetails(phoneNumber: "867-5309", phoneType: "home"),
PhoneDetails(phoneNumber: "1-900-649-2568", phoneType: "office")
]
)
var body: some View {
List {
ForEach($userPhonesManager.allPhones) { $phone in // <--- here
HStack {
Button(action: {
userPhonesManager.deletePhoneNumber(of: phone) // <--- here
}) {
Image(systemName: "minus.circle.fill")
}.buttonStyle(BorderlessButtonStyle())
TextField("Phone", text: $phone.phoneNumber) // <--- here
}
}
Button(action: { userPhonesManager.addNewPhoneNumber() }) {
Label {
Text("Add Phone Number")
} icon: {
Image(systemName: "plus.circle.fill")
}
}.buttonStyle(BorderlessButtonStyle())
}
}
}
SwiftUI TextField loses focus when styled
Because of the way your if
modifier is structured, SwiftUI is unable to see that the underlying View
is the same in the two conditions. For more detail on this, I'd suggest you watch Demystifying SwiftUI from WWDC 2021.
One solution is the simplify your border
modifier into the following:
.border(valid ? .clear : .red)
This way, SwiftUI can still tell that this is the same underlying View
.
How to detect when a TextField loses the focus in SwiftUI for iOS?
You can use the initializer init(_:text:onEditingChanged:onCommit:)
present in textfield. There you will be getting an action triggered when you begin editing and end editing. You can find out the minimal example below.
import SwiftUI
struct ContentView: View {
@State private var greeting: String = "Hello world!"
var body: some View {
TextField("Welcome", text: $greeting, onEditingChanged: { (editingChanged) in
if editingChanged {
print("TextField focused")
} else {
print("TextField focus removed")
}
})
}
}
Hope this helps.
SwiftUI run function on losing focus TextField
In iOS 15, @FocusState
and .focused
can give you this functionality. On iOS 13 and 14, you can use onEditingChanged
(shown in the second TextField
)
struct ContentView: View {
@State private var text = ""
@State private var text2 = ""
@FocusState private var isFocused: Bool
var body: some View {
TextField("Text goes here", text: $text)
.focused($isFocused)
.onChange(of: isFocused) { newValue in
if !newValue {
print("Lost focus")
}
}
TextField("Other text", text: $text2, onEditingChanged: { newValue in
print("Other text:",newValue)
})
}
}
How can I remove Textfield focus when I press return or click outside Textfield? (SwiftUI, MacOS)
Here are possible variants
import SwiftUI
import AppKit
struct ContentView: View {
@State var field1: String = "This is the Text Field View"
var body: some View {
VStack{
Button("Press") {
print("Button Pressed")
NSApp.keyWindow?.makeFirstResponder(nil)
}
TextField("Fill in Text", text: Binding(
get: { print("get") ; return self.field1 },
set: { print("set") ; self.field1 = $0 }
), onCommit: {
DispatchQueue.main.async {
NSApp.keyWindow?.makeFirstResponder(nil)
}
}
)
}
}
}
How to be notified when a TextEditor loses focus?
I figured it out, you can use the UIResponder.keyboardWillHideNotification
.
TextEditor(text: $notes)
.onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification)) { _ in
print("done editing!")
}
TextField in a list not working well in SwiftUI
Welcome to StackOverflow! Your issue is that you are directly updating an ObservableObject
in the TextField
. Every change you make to the model, causes a redraw of your view, which, of course, kicks your focus from the TextField
. The easiest answer is to implement your own Binding
on the TextField
. That will cause the model to update, without constantly redrawing your view:
struct OptionsView: View {
// You should be using @StateObject instead of @ObservedObject, but either should work.
@StateObject var model = PieChartViewModel()
@State var newText = ""
var body: some View {
NavigationView {
VStack {
List {
ForEach(model.options, id: \.self) { option in
Text(option)
}
}
List {
//Using Array(zip()) allows you to sort by the element, but use the index.
//This matters if you are rearranging or deleting the elements in a list.
ForEach(Array(zip(model.options, model.options.indices)), id: \.0) { option, index in
// Binding implemented here.
TextField(option, text: Binding<String>(
get: {
model.options[index]
},
set: { newValue in
//You can't update the model here because you will get the same behavior
//that you were getting in the first place.
newText = newValue
}))
.onSubmit {
//The model is updated here.
model.options[index] = newText
newText = ""
}
}
}
.navigationTitle("Options")
.toolbar {
ToolbarItem(placement: .bottomBar) {
Button {
addNewOption()
} label: {
HStack {
Image(systemName: "plus")
Text("Create a new option")
}
}
}
}
}
}
}
func addNewOption() {
model.options.insert("", at: model.options.count)
}
}
Related Topics
How to Split a String into a [String] and Not [Substring]
Command Failed Due to Signal: Segmentation Fault: 11 While Emitting Ir Sil Function
Why 'Self.Self' Compiles and Run in Swift
Swift Use Unicode Character in Localization.Strings
Classes in Swift Files Inside Folder References Not Seen by Xcode 10's Compiler
How to Add an Uicollectionviewlayout Programmatically
Making Sklabelnode as a Crop Node of Skshapenode
How to Obtain a Swift Type from a String
Loading Multiple Google Interstitial Ads Makes App Crash
Integer Literal Overflows When Stored into 'Int'
Codable Enum with Multiple Keys and Associated Values
How to Detect Text View Begin Editing and End Editing in Swift 3
Migration from Swift 3 to Swift 4 - Cannot Convert String to Expected String.Element
Non-Modular Headers of Openssl Library When Using Modulemap for Swift Framework
Swiftui Button Interact with Map
Realmswift Initialize List:Cannot Specialize a Non-Generic Definition