Take action when user hits return from textfield keyboard Xcode 14.2 - ios

In my app I have a textfield
TextField("", text: $messageText, axis: .vertical)
.submitLabel(.search)
.onSubmit{print("submitted")}
I want the return key to say "search" which it does with .submitLabel, and I want it to submit the entered text ($messageText) to my api (take action) and dismiss when search button is pressed, instead of simply adding another line to the text field.
Testing this by printing "submitted" shows no action has been taken in the console.
I have a button on the UI that does this:
Button {
sendMessage()
messageText = ""
isTyping.toggle()
isResponding.toggle()
}
I want the return button, now labeled "search" to take the action the Button is taking. I'm trying to use onSubmit but it does not get called, it enters another line in the textfield. I have created new views and tried a simple implementation of at textfield and onsbumit and I still only get an extra line added to the text field, and no on submit action.
How do I get the return/search key on a keyboard for the textfield to take action? I am using Xcode 14.2.
For reference here is the complete view block of code.
#ViewBuilder
func typingView() -> some View {
VStack {
HStack {
TextField("", text: $messageText, axis: .vertical)
.submitLabel(.search)
.onSubmit{ print("submitted")}
.padding()
.keyboardType(.webSearch)
.ignoresSafeArea(.keyboard)
.font(.system(size: 26, weight: .semibold))
.focused($typingInFocus)
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.75) {
self.typingInFocus = true
}
}
Button {
sendMessage()
messageText = ""
print("sent type")
isTyping.toggle()
print(isTyping)
isResponding.toggle()
}
label: {
Image(systemName: messageText == "" ? "circle" : "arrow.up.circle.fill")
.resizable()
.scaledToFit()
.frame(width: 25, height: 25)
.scaleEffect(messageText == "" ? 1.0 : 1.3)
.foregroundColor(messageText == "" ? .white.opacity(0.0) : .white)
.padding()
}
.disabled(messageText == "")
}
}
}

Related

Button style label image and text foreground color change at a different time?

I have a question about a part of my code in my custom button style. So I created a custom button style and this is what I am returning:
struct CustomStyle: ButtonStyle{
func makeBody(configuration: Configuration) -> some View {
return AnyView(Label {
configuration.label
} icon: {
Image(systemName: "plus").resizable().frame(width: 30, height: 30)
})
.foregroundColor(
configuration.isPressed ? Color.blue : Color.red
)
.padding()
.frame(maxWidth: .infinity, minHeight: 20)
.background(Color.yellow)
.clipShape(Capsule())
}
}
struct ContentView: View {
var body: some View {
VStack {
Button(action: {}, label: { Text("amin") })
.buttonStyle(CustomStyle())
}
}
}
The foregroundColor should change when I tap the button, and it does change. The issue is, the icon takes a few more milliseconds to go back to its original color. For example, let's say the color of the text and icon is red. When I click on the button both become blue, but text goes back to red immediately once I let go and icon(image) goes back to red with a very brief(a few millisecond) animation. I want both to be synced.
notes:
I know that most of the time in button styles we just return configuration.label, but what I am returning also works and has no issues. I have to do this because I want my button to have an image next to its text.
icon in this case is Image(systemName: "plus")
It is possible to fix by just disabling animation (tested with Xcode 13.4 / iOS 15.5)
struct CustomStyle: ButtonStyle{
func makeBody(configuration: Configuration) -> some View {
Label {
configuration.label
} icon: {
Image(systemName: "plus").resizable().frame(width: 30, height: 30)
}
.animation(.none, value: configuration.isPressed) // << here !!
.foregroundColor(
configuration.isPressed ? Color.blue : Color.red
)

swiftui: textfield in HStack, when tap the textfield, the code in ontapgesture of HStack is also executed

HStack {
textfield("", text: $text)
Text("Something")
}
.onTapGesture {
code
}
How can I focus the textfield without executing the codes in onTapGesture?
please help me, thanks a lot.
I come with following idea, it's simple and maybe temporary solution. Just Add onTapGesture Action on TextField so it will execute that and does not execute action on HStack.
HStack {
TextField("", text: $text).onTapGesture {
print("Tap On TextField")
}
Text("Something")
}
.onTapGesture {
print("Tap On HStack")
}
If we tap on Text("Something") it will execute HStack onTapGesture Action.
Update : -
WE can also do something like following :
HStack {
TextField("", text: $text).onTapGesture {
print("Tap On TextField")
}
Group {
Text("Something")
// Add Other Item here
}.onTapGesture {
print("Tap On Group")
}
}
This simply make 2 groups in Hstack and on Textfield Tap if you don't want any action then simply don't put any action on it. Put Your action on Other Group.

SwiftUI Dismiss keyboard on List NavigationLink item tap

I have a list of items and a text field for search keywords. When I do search on the list and tap on items, navigationlink works faster than keyboard dismiss, and this causes a disabled area in the next scroll view.
// search TextField
HStack {
Spacer(minLength: 10)
Image(systemName: "magnifyingglass")
.foregroundColor(.gray)
TextField("Search",
text: self.$textbox){
UIApplication.shared.endEditing()
}
.onChange(of: textbox) {
print($0)
dictionaryListVM.getResult(language: self.currentLanguage, text: self.textbox)
self.theId += 1
}
.accessibility(identifier: "loginUserName")
.background(Color.white)
.frame(height: 10, alignment: .leading)
.padding()
}.overlay(
RoundedRectangle(cornerRadius: 4)
.stroke(Color.gray, lineWidth: 2)
)
// Subview for displaying items:
GeometryReader { reader in
ScrollView {
VStack {
if model.dataLoaded {
List {
ForEach(self.model.englishDictionaries, id: \.id) { item in
ZStack {
VStack(alignment: .leading, spacing: 4) {
Text("\(item.value)").font(.title2)
Text("\(item.pairWord)").foregroundColor(.gray).font(.caption)
}
NavigationLink(destination: WordDetailView(englishWordId: item.id, englishWord: item.value, lang: "en")) {
}
.isDetailLink(false)
.buttonStyle(PlainButtonStyle()).frame(width:0).opacity(0)
}
}
}.frame(height: reader.size.height)
.animation(.linear)
}else{
Text("Words sync inprogress, please comeback later.")
}
}
}.padding(.leading, 10)
}
So, I want to make sure that my keyboard is dismayed before navigation to the next view.
check out demo for the issue in action
If you are using iOS 15
I believe you can make use of the #FocusState Property wrapper to dismiss the keyboard.
First you will have to define a variable to hold the focus value.
#FocusState private var isFocused: Bool
Adding the .focused(_:) View Modifier to the TextField:
TextField("Hello There", text: $someText)
.focused($isFocused)
Toggling $isFocused on button press or navigation link press. You can set it as shown below:
isFocused = false

Why alert is not showing up in swiftUI?

I am using .alert in swiftUI but its not showing up. below is my code --
Button(action: {
// Dismiss the PopUp
withAnimation(.linear(duration: 0.3)) {
print("On Submit clicked-------------->")
hideKeyboard()
if self.textFieldValidatorEmail(self.emailTxtField) {
self.isEmailValid = true
} else {
self.isEmailValid = false
// self.emailTxtField = ""
}
if !isEmailValid {
//return
} else {
LoginViewModel.params = ["user": nameTxtField.text, "email":emailTxtField.text]
self.viewModel.send(event: .onAppear)
}
}
}, label: {
Text("Submit")
.frame(width: 100, height: 40, alignment: .center)
.foregroundColor(Color.black)
.background(Color.yellow)
.font(Font.system(size: 19, weight: .semibold))
})
.alert(isPresented: $isEmailValid,
content: {
Alert(title: Text("Message"),
message: Text("downloadModel.alertMsg"),
dismissButton: .destructive(Text("Ok"),
action: {
withAnimation{
}
}))
})
I am not sure because I can't see your properties or full code if you can share with me your project through GitHub so I can take a more precise look, but as if now I believe you have to toggle the alert like to let the pop up know that you are triggering it or there must be some sort of .onAppear or onTapGesture condition, to make it equal to be the true value.
Long story short try to add a showing an alert condition and toggle the alert property somewhere in the action of your code.
Because I believe you did everything correctly my friend but the only thing you have to trigger is your alert somehow.

Swift - Updating Binding<String> stored value programmatically from View extension

So my goal is to have a more convenient method for adding a placeholder text value on SwiftUI's TextEditor, since there doesn't appear to be one. The approach I'm trying has uncovered something I really don't understand around Binding<> wrapped types. (Maybe this is a red flag that I'm doing something not recommended?)
Anyway, on to my question: are we able to programmatically update the underlying values on Bindings? If I accept some Binding<String> value, can I update it from within my method here? If so, will the updated value be referenced by the #State originator? The below example places my placeholder value in as text where I'm trying to type when you click into it, and does not even attempt it again if I clear it out.
Imported this code from other posts I found some time ago to make it display a placeholder if the body is empty.
import Foundation
import SwiftUI
struct TextEditorViewThing: View {
#State private var noteText = ""
var body: some View {
VStack{
TextEditor(text: $noteText)
.textPlaceholder(placeholder: "PLACEHOLDER", text: $noteText)
.padding()
}
}
}
extension TextEditor {
#ViewBuilder func textPlaceholder(placeholder: String, text: Binding<String>) -> some View {
self.onAppear {
// remove the placeholder text when keyboard appears
NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: .main) { (noti) in
withAnimation {
if text.wrappedValue == placeholder {
text.wrappedValue = placeholder
}
}
}
// put back the placeholder text if the user dismisses the keyboard without adding any text
NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillHideNotification, object: nil, queue: .main) { (noti) in
withAnimation {
if text.wrappedValue == "" {
text.wrappedValue = placeholder
}
}
}
}
}
}
Customize this setup as per your requirement:
struct ContentView: View {
#State private var text: String = ""
var body: some View {
VStack {
ZStack(alignment: .leading) {
if self.text.isEmpty {
VStack {
Text("Placeholder Text")
.multilineTextAlignment(.leading)
.padding(.leading, 25)
.padding(.top, 8)
.opacity(0.5)
Spacer()
}
}
TextEditor(text: $text)
.padding(.leading, 20)
.opacity(self.text.isEmpty ? 0.5 : 1)
}
.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height/2)
.overlay(
Rectangle().stroke()
.foregroundColor(Color.black)
.padding(.horizontal, 15)
)
}
}
}

Resources