I'm trying to give a background color to a UITextField using SwiftUI since I'm trying to use some unique colours in my app to support both LightMode and DarkMode.
My colors are always defined as a ColorSet in the xcassets folder and this is the code I was using at first to achieve this background color.
TextField("Exam title", text: $title)
.padding()
.background(Color("cardBackground"))
.cornerRadius(8)
This way I'm able to change the background color of the TextField when I'm not using it.
This is how's the outcome of this
Correct Look
The problem I'm facing is that as soon as I tap on the TextField it goes back to its default color (I think it is the default one) and I'm unable to change that.
When editing
So what I did was creating a UIViewRepresentable implementation of the TextField, maybe that could have helped me much more than SwiftUI can do at this stage.
struct CustomUIKitTextField: UIViewRepresentable {
#Binding var text: String
var placeholder: String
var backgroundColor: UIColor = .red
func makeUIView(context: UIViewRepresentableContext<CustomUIKitTextField>) -> UITextField {
let textField = UITextField(frame: .zero)
textField.delegate = context.coordinator
textField.placeholder = placeholder
textField.backgroundColor = backgroundColor
return textField
}
func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext<CustomUIKitTextField>) {
uiView.text = text
uiView.backgroundColor = backgroundColor
uiView.setContentHuggingPriority(.defaultHigh, for: .vertical)
uiView.setContentCompressionResistancePriority(.required, for: .vertical)
}
func makeCoordinator() -> CustomUIKitTextField.Coordinator {
Coordinator(parent: self)
}
class Coordinator: NSObject, UITextFieldDelegate {
var parent: CustomUIKitTextField
init(parent: CustomUIKitTextField) {
self.parent = parent
}
func textFieldDidChangeSelection(_ textField: UITextField) {
parent.text = textField.text ?? ""
}
func textFieldDidBeginEditing(_ textField: UITextField) {
print("Begin editing")
textField.backgroundColor = .brown
}
func textFieldDidEndEditing(_ textField: UITextField) {
print("Finished Editing")
}
}
}
I've tried something as you can see (there are some things to just debug stuff) but I'm not that much experienced in UIKit so I don't really know what is the best way to tackle this problem and out there there's not much about this kind of problem.
Have you faced something similar before? How can I solve this problem?
EDIT:
If this can help this is the view hierarchy when the TextField is in editing mode and the selected elements that puts itself in front of the TextField is a UIFieldEditor
UIFieldEditor
first off, don't define background colour in SwiftUI:
.background(Color("cardBackground")) / remove this line of code
In the makeUIView method, setup a default background color, which is used, when user not tapping the textfield:
textfield.backgroundColor = .systemGray
Finally, use these methods in the Coordinator class to control background colour behaviour:
func textFieldDidBeginEditing(_ textField: UITextField) {
textField.backgroundColor = .red
}
func textFieldDidEndEditing(_ textField: UITextField, reason: UITextField.DidEndEditingReason) {
textField.backgroundColor = .systemGray
}
Related
I am new in IOS and working on the SwiftUI. I wrapped UITextField in the SwiftUI using UIViewRepresentable. But the issue is:
I am unable to fix the wrapped UITextField width and height. The text writes only in one line and increases the width of the UITextfield when writing more text.
Here is my code:
struct SecondWrappedTextField: UIViewRepresentable {
#Binding var text: String // Declare a binding value
func makeUIView(context: Context) -> UITextField {
let textField = UITextField()
textField.delegate = context.coordinator
textField.textAlignment = .left
textField.contentVerticalAlignment = .top
return textField
}
func updateUIView(_ uiView: UITextField, context: Context) {
uiView.text = text // 1. Read the binded
print("Selcted Text")
let getSelectedText=UserDefaults.standard.string(forKey: text) ?? ""
let mainString = text
let stringToColor = getSelectedText
let range = (mainString as NSString).range(of: stringToColor)
let mutableAttributedString = NSMutableAttributedString.init(string: mainString)
mutableAttributedString.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor.red, range: range)
uiView.attributedText = mutableAttributedString
}
func makeCoordinator() -> Coordinator {
return Coordinator(text: $text)
}
class Coordinator: NSObject, UITextFieldDelegate,ObservableObject {
#Binding var text: String
init(text: Binding<String>) {
self._text = text
}
func textFieldDidChangeSelection(_ textField: UITextField) {
DispatchQueue.main.async {
self.text = textField.text ?? "" // 2. Write to the binded
let abc = textField // 2. Write to the binded
print("TEXT")
print(self.text)
}
}
}
}
SecondWrappedTextField(text: $textNote).multilineTextAlignment(.leading).background(Color.white).frame(width:200,height:200)
Sorry for the bad English.[Image]
UITextField : For entering a single line of text. Use secure entry
for sensitive data such as passwords.
UITextView : For displaying or
entering one or more lines of text.
So if you want to have multiline editable text field, you should use UITextView as UIViewRepresentable.
This is an example tutorial by Prafulla Singh, it works amazingly in my project.
https://betterprogramming.pub/swiftui-multiline-content-fit-textfield-3abbdcedaabc
I want to create an error on a textfield validation very similar to this one:
I understood there is some native iOS red icon that I can show on the validation error?
How can I achieve something like that?
Do you know of a library or a piece of code to achieve that?
No, there isn't any native iOS validation error indicator, you have to use your own image.
So you can set UIImageView with your own validation error image as rightView of your TextField
yourTextField.rightView = UIImageView(image: UIImage(named: "yourImage"))
yourTextField.rightViewMode = .always
Now inside some delegate method set isHidden of rightView of your TableView depending on your condition
func textFieldDidEndEditing(_ textField: UITextField) { // for example when editing did end
textField.rightView?.isHidden = isEverythingCorrect
}
1- Assign rightView to UITextField
2- Conform to UITextFieldDelegate
class ViewController:UIViewController, UITextFieldDelegate {
#IBOutlet weak var textfield: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.textfield.delegate = self
let img = UIImageView(image: UIImage(named: "02"))
img.frame = CGRect(x: 0, y: 0, width: 20, height: 20)
textfield.rightView = img
textfield.rightViewMode = .always
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let content = (textField.text! as NSString).replacingCharacters(in: range, with: string)
textField.rightView!.isHidden = content.count % 2 == 0
return true
}
}
I have four UITextFields, each of which represents a single digit of an OTP. I want the control to shift to consecutive textfield as the user types in the code. My implementation is below.
extension FillSignUpCodeController: UITextFieldDelegate {
func textFieldDidBeginEditing(_ textField: UITextField) {
textField.text = ""
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let inputString = (textField.text! as NSString).replacingCharacters(in: range, with: string)
if inputString.count == 1 {
switch textField {
case textFieldCodeFirstDigit:
textFieldCodeFirstDigit.text = inputString
textFieldCodeFirstDigit.resignFirstResponder()
textFieldCodeSecondDigit.becomeFirstResponder()
case textFieldCodeSecondDigit:
textFieldCodeSecondDigit.text = inputString
textFieldCodeSecondDigit.resignFirstResponder()
textFieldCodeThirdDigit.becomeFirstResponder()
case textFieldCodeThirdDigit:
textFieldCodeThirdDigit.text = inputString
textFieldCodeThirdDigit.resignFirstResponder()
textFieldCodeFourthDigit.becomeFirstResponder()
case textFieldCodeFourthDigit:
textFieldCodeFourthDigit.text = inputString
textFieldCodeFourthDigit.resignFirstResponder()
default:
return false
}
}
return true
}
}
With this piece of code, as the user types the first digit, the first textfield takes the input value and moves the control to the next textfield. However, the second text field is taking the value of the first digit. I tried setting the text to empty after changing the firstResponder but it did not work. How can I fix this issue? Thanks.
Since textField(_:shouldChangeCharactersIn:replacementString:) executes before the text is present in the text field, you should put the code inside a method that is called when the text did already change.
You should observe the changes of your text field and execute the responder-changing code there. You can find more information about that in this question.
Moreover, resigning the first responder before changing it is redundant, you don't need to do that.
It is also very redundant to handle every text field separately. I'd recommend including your text fields in an array and iterating through them.
shouldChangeCharactersInRange gets called before the textField is filled. You can do it by:
textField.addTarget(self, action: #selector(textFieldChangedValue(textField:)), for: UIControlEvents.editingChanged)
write above line for all textfields in viewDidLoad()
#objc func textFieldChangedValue(textField: UITextField) {
print(textField.text)
}
This will work
Following the answers from #the4kman and #Rahul Dasgupta, I have implemented the following:
FillUpCodeViewController.swift
override func viewDidLoad() {
setupArrrayofTextFields(textField1: textFieldCodeFirstDigit,
textField2: textFieldCodeSecondDigit,
textField3: textFieldCodeThirdDigit,
textField4: textFieldCodeFourthDigit)
}
func setupArrrayofTextFields(textField1: UITextField, textField2: UITextField,
textField3: UITextField, textField4: UITextField) {
arrayOftextFields = [textField1, textField2, textField3, textField4]
for textField in arrayOftextFields {
textField.addTarget(self, action: #selector(textFieldDidChange(textField:)), for: .editingChanged)
}
}
#objc func textFieldDidChange(textField: UITextField) {
guard textField.text?.count == 0 else {
let index: Int = arrayOftextFields.index(of: textField)!
guard index == (arrayOftextFields.count-1) else {
arrayOftextFields[index+1].becomeFirstResponder()
return
}
textField.resignFirstResponder()
return
}
}
And, again in the viewcontroller in which I have to implement the submission of recovery code, I simply inherited the FillUpCodeViewController class.
RecoveryViewController.swift
override func viewDidLoad() {
setupArrrayofTextFields(textField1: textFieldRecoveyCodeFirstDigit,
textField2: textFieldRecoveyCodeSecondDigit,
textField3: textFieldRecoveyCodeThirdDigit,
textField4: textFieldRecoveyCodeFourthDigit)
}
I'm using a UITextField to show results of a calculation but I don't want the keyboard to appear when the user taps on the UITextField.
I'm using a UITextField because I still want the user to be able to Copy and Paste the calculation back into the UITextField, but I don't want the keyboard to show up.
UIKeyboardWillHide only works after the keyboard is displayed.
Swift 4.2, This works for me.
put in viewDidLoad()
//It will Hide Keyboard
textField.inputView = UIView()
//It will Hide Keyboard tool bar
textField.inputAccessoryView = UIView()
//It will Hide the cursor
textField.tintColor = .white
Its quite simple to do with UITextField. Use this code in viewDidLoad()
self.txtresult.inputView = UIView()
self.txtresult.inputAccessoryView = UIView()
For iPads, according to this response of #Awais Jamil, add the following code
textField.inputAssistantItem.leadingBarButtonGroups = []
textField.inputAssistantItem.trailingBarButtonGroups = []
First set delegate with your UITextField in self class.
You can do with below 2 ways.
1. From storyboard
2. From Code ( You can write at viewDidLoad() )
textField.delegate = self
Then declare protocol UITextFieldDelegate in your class.
Now call delegate method.
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
self.view.endEditing(true)
return true
}
textField.inputView = UIView()
This line of code in your textFieldDidBeginEditing func will do the job.
My func:
func textFieldDidBeginEditing(_ textField: UITextField) {
keyboardView.activeTextField = textField
textField.inputView = UIView()
}
You can hide keyboard in UITextFieldDelegate method textFieldDidBeginEditing:(textField: UITextField) like below :
func textFieldDidBeginEditing:(textField: UITextField) {
self.view.endEditing(true)
}
you could use an "empty" inputview like this:
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
let inputView = UIView(frame: .zero)
inputView.backgroundColor = UIColor.clearColor()
inputView.opaque = false
textField.inputView = inputView
return true
}
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
self.view.endEditing(true)
return false
}
You can try this as well VIA text field delegate method.
func textFieldDidBeginEditing(_ textField: UITextField) {
textField.resignFirstResponder()
// your custom impl
}
I have placeholder text in a UITextField. If someone writes the placeholder text disappears which is what I would like. But then if they delete that writing the placeholder text doesn't reappear. I am trying to work out how to make it reappear if after editing the textfield is empty.
My app is in swift.
An extension like this should provide what you want. If need be, you can also move the logic to textFieldShouldReturn, if that suits your app better:
extension ViewController: UITextFieldDelegate {
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
guard let text = textField.text, !text.isEmpty else {
textField.placeholder = "Your placeholder text"
return true
}
return true
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
}