Textfield in Swiftui Loses Focus When I Enter a Character

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 ObservableObjectss 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



Leave a reply



Submit