SwiftUI equivalent to textFieldShouldReturn - swift

Is there a SwiftUI equivalent for textFieldShouldReturn? I would like my app to run a command after I press the return/enter key in a TextField. macOS App

For Big Sur, just use TextField's onCommit closure. For later versions, check out onSubmit(of:_:).
struct ContentView: View {
#State var text = ""
var body: some View {
TextField("Text", text: $text) { isEditing in
print("Textfield is editing: \(isEditing)") /// in case you want to update your UI
} onCommit: {
print("Return pressed") /// for your logic
}
}
}

Related

how to detect when clear button tapped on SwiftUI Textfield with Introspect

I'm using Introspect on a SwiftUI TextField to add a clear button to the text field. When the clear button is tapped, I want to toggle a boolean in my view model.
TextField("", text: $viewModel.title)
.textFieldStyle(.roundedBorder)
.introspectTextField(customize: { $0.clearButtonMode = .always })
.focused($isFocused)
.submitLabel(.done)
How can execute code when the clear button is tapped? Introspect makes UIKit methods available for the Swift UI Text Field. So the question might really be how do I execute some code on the tap of the clear button on a UIKIt UITextField.
Try this, make a view modifier:
struct ClearButton: ViewModifier {
#Binding var text: String
func body(content: Content) -> some View {
HStack {
content
if !text.isEmpty {
Button(
action: { self.text = "" },
label: {
Image(systemName: "delete.left")
.foregroundColor(Color(UIColor.opaqueSeparator))
}
)
}
} }
}
and the ContentView
struct ContentView: View {
#State var sampleText: String = ""
var body: some View {
NavigationView {
Form {
Section {
TextField("Type in here...", text: $sampleText)
.modifier(ClearButton(text: $sampleText))
.multilineTextAlignment(.leading)
}
}
.navigationTitle("Clear example")
}
}
}
You can execute what you want in the Button's action:

SwiftUI - onCommit equivalent for TextEditor

Is there an onCommit equivalent for TextEditor in SwiftUI? I want to dismiss the keyboard by using self.endEditing and this can easily be achieved with text fields by using onCommit.
I've tried using the onTapGesture method where i call UIApplication.self.endEditing() when a user taps anywhere outside the text field, however this causes some unwanted bugs with my existing segmented picker so I'd like to avoid that.
import SwiftUI
import PlaygroundSupport
struct TestView: View {
#State private var fullText: String = "This is some editable text..."
var body: some View {
VStack {
Spacer()
TextEditor(text: $fullText)
}.onTapGesture {
closeView()
}
}
func closeView() {
UIApplication.shared.sendAction(
#selector(UIResponder.resignFirstResponder),
to: nil,
from: nil,
for: nil
)
}
}
let view = TestView()
PlaygroundPage.current.setLiveView(view)
Give it a go with resignFirstResponder
SwiftUI - Dismiss the keyboard on TextEditor
When user press enter or creates a new line.
TextEditor(text: $text)
.focused($isFocused)
.onChange(of: text) { string in
if string.last == "\n"
{
text.removeLast()
isFocused = false
}
}
When user taps.
TextEditor(text: $text)
.focused($isFocused)
.onTapGesture {
isFocused ? (isFocused = false) : (isFocused = true)
}
Also you can create a function to hide keyboard and replace it where focus assignation is. But I think is not necessary here.

Setting TextField /TextEditor as the first responder upon appearing SwifUI

I've got a SwiftUI TextEditor that appears once a button is pressed. What I can't figure out is, how to make the TextEditor the first responder when it appears.
What I've tried is adding a .focused modifier to the TextEditor and then setting the focused Bool value to true inside .onAppear. But still the keyboard only shows up when its pressed.
import SwiftUI
struct ContentView: View {
#State var showTextField : Bool = false
#State private var currentEditText : String = "Some text"
#FocusState private var editTextFieldFocus: Bool
var body: some View {
VStack {
Button {
showTextField.toggle()
} label: {
Text("Show Text Field")
}
Text("Hello, world!")
.padding()
if showTextField{
TextEditor(text: $currentEditText)
.focused($editTextFieldFocus)
.onAppear {
editTextFieldFocus = true
}
}
}
}
}

TextField with animation crashes app and looses focus

Minimal reproducible example:
In SceneDelegate.swift:
let contentView = Container()
In ContentView.swift:
struct SwiftUIView: View {
#State var text: String = ""
var body: some View {
VStack {
CustomTextFieldView(text: $text)
}
}
}
struct Container: View {
#State var bool: Bool = false
#State var text: String = ""
var body: some View {
Button(action: {
self.bool.toggle()
}) {
Text("Sheet!")
}
.sheet(isPresented: $bool) {
SwiftUIView()
}
}
}
In CustomTextField.swift:
struct CustomTextFieldView: View {
#Binding var text: String
#State var editing: Bool = false
var body: some View {
Group {
if self.editing {
textField
.background(Color.red)
} else {
ZStack(alignment: .leading) {
textField
.background(Color.green)
Text("Placeholder")
}
}
}
.onTapGesture {
withAnimation {
self.editing = true
}
}
}
var textField: some View {
TextField("", text: $text)
}
Problem:
After running the above code and focusing the text field, the app crashes. Some things I noticed:
If I remove the withAnimation code, or the ZStack in CustomTextField file, the app doesn't crash, but the TextField looses focus.
If I remove the VStack in SwiftUIView, the app doesn't crash, but the TextField looses focus.
If I use a NavigationLink or present the TextField without a sheet, the app doesn't crash, but the TextField looses focus.
Questions:
Is this a problem in the current version of SwiftUI?
Is there a solution to this problem using SwiftUI? I want to stay out of
ViewRepresentables as much as possible.
How can I keep the focus of the TextField after the body is recalculated because of a change in state?
How can I keep the focus of the TextField after the body is
recalculated because of a change in state?
You have two of them. Two different TextField could not be in editing state at the same time.
The approach suggested by Asperi is the only possible.
The reason, why your code crash is not easy explain, but expected in current SwiftUI.
You have to understand, that Group is not a standard container, it just like a "block" on which you can apply some modifiers. Removing Group and using wraping body in ViewBuilder
struct CustomTextFieldView: View {
#Binding var text: String
#State var editing: Bool = false
#ViewBuilder
var body: some View {
if self.editing {
TextField("", text: $text)
.background(Color.red)
.onTapGesture {
self.editing.toggle()
}
} else {
ZStack(alignment: .leading) {
TextField("", text: $text)
.background(Color.green)
.onTapGesture {
self.editing.toggle()
}
}
}
}
}
the code will stop to crash, but there is other issue, the keyboard will dismiss immediately. That is due the tap gesture applied.
So, believe or not, you have to use ONE TextField ONLY.
struct CustomTextFieldView: View {
#Binding var text: String
#State var editing = false
var textField: some View {
TextField("", text: $text, onEditingChanged: { edit in
self.editing = edit
})
}
var body: some View {
textField.background(editing ? Color.green : Color.red)
}
}
Use this custom text field elsewhere in your code, as you want
Try the following for CustomTextFieldView (tested & works with Xcode 11.2 / iOS 13.2)
struct CustomTextFieldView: View {
#Binding var text: String
#State var editing: Bool = false
var body: some View {
TextField("", text: $text)
.background(self.editing ? Color.red : Color.green)
.onTapGesture {
withAnimation {
self.editing = true
}
}
}
}
How can I keep the focus of the TextField after the body is recalculated because of a change in state?
You don't loose focus, you just remove entire text field, so the solution is not replace text field, but modify its property, ie background. It's ok to put it into ZStack, but keep it one.

Swiftui: how to watch variable binded to text field always

I am using Swift-UI for creating my app.
There is an AccountView is listing user's attributes and you can update it.
Once you click an Update button on the user's variable row of the list, navigate to EditVariableView, where you can change the variable with Text Field.
Of course, the text field has a validation of the inputted text, and you can commit the change by the Submit button on the right-up corner of EditVariableView.
For validation of the input, I use onCommit, detecting the change of the input, but here is a problem.
When you touch the text field, the keyboard comes out, and also you can input the text. But onCommit emits an event only when you close the keyboard.
If you input the text and click the Submit button without closing the keyboard, certainly onCommit does not emit an event for the validation. So, of course, the validation won't be done.
I want you to tell me, how to detect the input change on every text change.
You can disable Submit button if TextField is in editing state
import SwiftUI
struct ContentView: View {
#State var txt: String = ""
#State var editingFlag = false
var body: some View {
VStack {
TextField("text", text: $txt, onEditingChanged: { (editing) in
self.editingFlag = editing
}) {
print("commit")
}.padding().border(Color.red)
Button(action: {
print("submit")
}) {
Text("SUBMIT")
}.disabled(editingFlag)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
SwiftUI 2.0
With the Xcode 12 and from iOS 14, macOS 11, or any other OS contains SwiftUI 2.0, there is a new modifier called 'onChange' that detects any change of the given state and can be performed on any view. So with some minor refactoring:
struct ContentView: View {
#State var txt: String = ""
#State var editingFlag = false
var body: some View {
VStack {
TextField("text", text: $txt)
.onChange(of: txt) {
print("Changed to :\($0)")
}
}
.padding()
.border(Color.red)
Button("SUBMIT") {
print("submit")
}
.disabled(editingFlag)
}
}
from: hacking with with Swift. Paul Hudson
struct ContentView : View {
#State private var name = ""
var body: some View {
TextField("Enter your name:", text: $name)
.textFieldStyle(RoundedBorderTextFieldStyle())
.onChange(of: name) { newValue in
print("Name changed to \(name)!")
}
}
}