textFieldDidEndEditing is not called and validations are not checked - ios

I am creating a sign up view(form) with Fullname, Email, Password and Verify Password as textfields. And I want to use validations as "mandatory field" incase of error while user inputs the data. I have created extension for UITextField and String. But I found that textFieldDidEndEditing is not called and validations are not checked. Anyone help me to solve this issue ?
//UITextField
import UIKit
extension UITextField {
func addLeftPadding() {
let view = UIView(frame:CGRect(x: 0, y: 0, width: 10, height: 40))
self.leftView = view
self.leftViewMode = .always
}
func addWarning(warningMessage message:String) {
self.removeWarning()
let label = UILabel(frame: CGRect(x:self.frame.minX,y: self.frame.maxY + 2 , width:self.frame.width,height:15))
label.tag = self.tag
label.text = message
self.superview?.addSubview(label)
}
func removeWarning() {
for subView in (self.superview?.subviews)! {
if let label = subView as? UILabel , label.tag == self.tag {
label.removeFromSuperview()
}
}
}
}
//String
import UIKit
extension String {
var isEmail: Bool {
let emailRegEx = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}"
let emailTest = NSPredicate(format:"SELF MATCHES %#", emailRegEx)
return emailTest.evaluate(with: self)
}
}
//SignUpController
import UIKit
import Material
class SignUpController: UIViewController {
#IBOutlet weak var fullName: TextField!
#IBOutlet weak var Email: TextField!
#IBOutlet weak var PasswordField: TextField!
#IBOutlet weak var NewPassword: TextField!
override func viewDidLoad() {
super.viewDidLoad()
prepareFullNameField()
prepareEmailField()
preparePasswordField()
prepareNewPasswordField()
self.fullName.delegate = self
self.Email.delegate = self
self.PasswordField.delegate = self
self.NewPassword.delegate = self
fullName.leftViewMode = UITextFieldViewMode.always
fullName.returnKeyType = UIReturnKeyType.next
Email.leftViewMode = UITextFieldViewMode.always
Email.returnKeyType = UIReturnKeyType.next
PasswordField.leftViewMode = UITextFieldViewMode.always
PasswordField.returnKeyType = UIReturnKeyType.next
NewPassword.leftViewMode = UITextFieldViewMode.always
NewPassword.returnKeyType = UIReturnKeyType.done
//prepareResignResponderButton()
// Do any additional setup after loading the view.
}
}
extension SignUpController {
fileprivate func prepareFullNameField() {
fullName.placeholder = "Name"
fullName.textColor = UIColor.white
}
fileprivate func prepareEmailField() {
Email.placeholder = "Email"
Email.textColor = UIColor.white
}
fileprivate func preparePasswordField() {
PasswordField.placeholder = "Password"
PasswordField.textColor = UIColor.white
}
fileprivate func prepareNewPasswordField() {
NewPassword.placeholder = "Verify Password"
NewPassword.textColor = UIColor.white
}
}
extension SignUpController: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool { //delegate method
self.view.endEditing(true)
if textField == fullName {
fullName.resignFirstResponder()
//Email.becomeFirstResponder()
Email.becomeFirstResponder()
}
else if textField == Email {
Email.resignFirstResponder()
PasswordField.becomeFirstResponder()
}
else if textField == PasswordField {
PasswordField.resignFirstResponder()
NewPassword.becomeFirstResponder()
}
else{
NewPassword.resignFirstResponder()
}
return true
}
func dismissKeyboard() {
fullName.resignFirstResponder()
Email.resignFirstResponder()
PasswordField.resignFirstResponder()
NewPassword.resignFirstResponder()
}
// public func textFieldDidEndEditing(_ textField: UITextField) {
// (textField as? ErrorTextField)?.isErrorRevealed = false
// print("none")
// }
func textFieldDidEndEditing(_ textField: UITextField) {
switch textField {
case fullName:
if fullName == nil {
textField.addWarning(warningMessage: "mandatory_field")
print("hmmmmmmmmm")
}else{
textField.removeWarning()
}
case Email:
if Email == nil {
textField.addWarning(warningMessage:"mandatory_field")
}else{
textField.removeWarning()
}
case PasswordField:
if PasswordField == nil {
textField.addWarning(warningMessage: "invalid_email")
}else{
textField.removeWarning()
}
case NewPassword:
if NewPassword == nil {
textField.addWarning(warningMessage: "minimum_password")
}else{
textField.removeWarning()
}
default:
print("default")
}
}
public func textFieldShouldClear(_ textField: UITextField) -> Bool {
(textField as? ErrorTextField)?.isErrorRevealed = false
print("good")
return true
}
// func textFieldShouldReturn(_ textField: UITextField) -> Bool {
// (textField as? ErrorTextField)?.isErrorRevealed = true
//
// print("ok")
// return true
// }
}

Please try the below code to validate the text fields.
if (textField.text.isEmpty) {
let alert = UIAlertView()
alert.title = "No Text"
alert.message = "Please Enter Text In The Box"
alert.addButtonWithTitle("Ok")
alert.show()
}
I have also used the same code for validating text field in Swift version 3.

Related

Enable a button if all the UITextFields are filled and one of them has an Int value

I have problem with associating the textField to my shouldChangeCharactersIn func, and this textField should contain Int values. First textField should contain String value, and second should contain Int value. Confirm button should be enabled only if textFields are filled with correct types. That's my app:
And code:
import UIKit
class LoginViewController: UIViewController, UITextFieldDelegate {
var av = ActualValues()
var statsVC = StatsViewController()
var closureBlock: (() -> Void)?
#IBOutlet weak var idTextField: UITextField!
#IBOutlet weak var idCheckButton: UIButton!
#IBOutlet weak var segmentedControl: UISegmentedControl!
#IBOutlet weak var targetsTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
statsVC.allRunsFunc()
idTextField.delegate = self
idCheckButton.isEnabled = false
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
av.shift = 1
allRunsFunc()
}
#IBAction func idCheckButtonDidPress(_ sender: UIButton) {
av.id = idTextField.text!
av.databaseID = String(runs + 1)
av.target = targetsTextField.text!
closureBlock?()
if let delegate = self.summaryButtonIsEnabledDelegate {
delegate.toggleSummaryButton()
}
self.dismiss(animated: true, completion: nil)
}
#IBAction func segmentedControlChanged(_ sender: UISegmentedControl) {
switch segmentedControl.selectedSegmentIndex {
case 0:
av.shift = 1
case 1:
av.shift = 2
case 2:
av.shift = 3
default:
av.shift = 1
}
}
func configureID(model: ActualValues) {
av = model
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String, int: Int) -> Bool {
let text = (idTextField.text! as NSString).replacingCharacters(in: range, with: string)
if text.isEmpty {
idCheckButton.isEnabled = false
} else {
idCheckButton.isEnabled = true
}
return true
}
}
What should I add to that func?
Connect this #IBAction to both text fields, for Editing Changed:
#IBAction func textFieldEdited(_ sender: Any) {
// reset to disabled every time a text field value changes
// will be set to enabled if data is validated
idCheckButton.isEnabled = false
// make sure idTextField has text
guard let idText = idTextField.text, idText != "" else {
return
}
// make sure targetsTextField has text
guard let targetsText = targetsTextField.text, targetsText != "" else {
return
}
// make sure targetsTextField has only numbers
guard targetsText.rangeOfCharacter(from: CharacterSet.decimalDigits.inverted) == nil else {
return
}
// enable the button
idCheckButton.isEnabled = true
}
This will be called with every character entered in either of the fields. It:
first resets the button to .isEnabled = false
then checks to see if the id text field has text
then checks to see if the targets text field has text
then checks to see that the targets field has only numbers
If it makes it through all 3 checks, it sets the button to .isEnabled = true
Try create a function call validator() . all your textField should be add
textField.addTarget(self, action: #selector(self.textFieldDidChange(_:)), for: .editingChanged)
#objc func textFieldDidChange(_ textField: UITextField) {
// TODO : Validate all of your textField value here
}
Instead of
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String, int: Int) -> Bool {
let text = (idTextField.text! as NSString).replacingCharacters(in: range, with: string)
if text.isEmpty {
idCheckButton.isEnabled = false
} else {
idCheckButton.isEnabled = true
}
return true
}
Try this
// Add these variables
var idValid = false
var targetsValid = false
func textFieldDidChangeSelection(_ textField: UITextField) {
if textField == idTextField {
// Verify id has text
if textField.text! != "" {
idValid = true
} else {
idValid = false
}
}
if textField == targetsTextField {
// Verify targets is numeric
if textField.text!.isNumeric {
targetsValid = true
} else {
targetsValid = false
}
}
if idValid && targetsValid {
// Both valid enable
idCheckButton.isEnabled = true
} else {
// Not both valid disable
idCheckButton.isEnabled = false
}
}
And you will have to add this extension for String to verify a String as a numeric value - add a new blank Swift file to your project called 'StringExtension' and add:
import Foundation
import UIKit
extension String {
var isNumeric: Bool {
guard self.count > 0 else { return false }
let nums: Set<Character> = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
return Set(self).isSubset(of: nums)
}
}

Swift: How can I store array data, so it can be accessed more than once after leaving class?

I'm currently trying to develop a prototype for a quiz application.
The questions for the quiz are read out of a JSON file and the answers are generated within the program. Those two parts are then matched to each other within a Mediator class.
Displaying the question in my ViewController works perfectly fine for the first question. When trying to retrieve the second question the array that stores the question in the Mediator class is empty. That obviously is the case, because I'm re-entering the Mediator after going to my ViewController.
How can I store the data more efficiently?
If you need more information, please let me know. I'm really grateful for every suggestion I can get. I'm totally stuck at right now!
ViewController:
[...]
override func viewDidLoad() {
super.viewDidLoad()
setup(categoryType)
navigationBar.title = barTitle
QAMatcher.delegate = self
QAMatcher.getCategory(categoryType)
}
#objc func didChooseAnswer(_ sender: UIButton){
QAMatcher.nextQuestion()
updateUI()
}
func updateUI(){
if let label = questionLabel {
label.text = QAMatcher.getQuestion()
}
if let type = AnswerType(rawValue: QAMatcher.getAnswerType()) {
addAnswerSection(type)
}
}
func addAnswerSection(_ type: AnswerType) {
if let stackView = quizStackView, let answerSection = uiBuilder.getAnswerSection(answerType: type) {
stackView.addArrangedSubview(answerSection)
}
}
[...]
Mediator Class:
import Foundation
protocol Mediator {
func sendRequest (_ request: Request, sender: Sender)
}
protocol MediatorDelegate {
func didUpdate(mediator: QuestionAnswerMediator, _ array: [QuestionData])
}
enum Sender {
case answer
case question
}
class QuestionAnswerMediator: Mediator {
var delegate: MediatorDelegate?
var questionArray = [QuestionData]()
var answerArray = [String]()
var request = Request()
var questionNum = 0
func getCategory(_ category: CategoryType) {
request.categoryType = category
matchQuestionAndAnswers()
}
func sendRequest (_ request: Request, sender: Sender){
if sender == .question {
let array = QuestionManager.shared.send(category: request.categoryType!)
questionArray.append(contentsOf: array)
}
else if sender == .answer {
let array = AnswerManager.shared.send(request: request)
if array?.isEmpty == false {
answerArray = []
answerArray.append(contentsOf: array ?? ["default"])
}
}
}
func fetchAnswers(for type: String, question: String) {
request.answerType = AnswerType(rawValue: type)
request.question = question
sendRequest(request, sender: .answer)
}
func fetchQuestions (for category: CategoryType){
request.categoryType = category
sendRequest(request, sender: .question)
}
func matchQuestionAndAnswers(){
var i = 0
fetchQuestions(for: request.categoryType!)
for question in questionArray {
fetchAnswers(for: question.answerType, question: question.question)
if answerArray.isEmpty != true {
questionArray[i].answers = answerArray
}
i += 1
}
self.delegate?.didUpdate(mediator: self, questionArray)
}
func getQuestion() -> String {
return questionArray[questionNum].question
}
func getAnswers() -> [String] {
return questionArray[questionNum].answers!
}
func getAnswerType() -> String {
return questionArray[questionNum].answerType
}
func nextQuestion () {
if questionNum + 1 < questionArray.count {
questionNum += 1
}
else {
questionNum = 0
}
}
}
ok, that was hard ;) i had to create my own project file, because yours was still missing. After doing that i tried to compile and run...but you did not fill the assets so it crashed. After faking your assets...it runs. ;)
You already analyzed your problem correctly. You should never create a viewcontroller mutliple times if you just call him once. So i deleted this and you now only have one instance.
i routed the "didchooseAnswer" through your classes...but you still have some work to do: i had to give a frame to your view class, so fill that frame you need, i don't know what frame you need.
I just corrected it for MultipleChoiceView...you have to do it for the other views as well like PolarView etc...
i hope i have everything copied what i changed, if not, just tell me. but maybe then you should give me rights on your github project so that i can commit it there...
Quizviewcontroller:
import UIKit
import CoreLocation
import os
class QuizViewController: UIViewController {
#IBOutlet weak var questionLabel: UILabel!
#IBOutlet weak var navigationBar: UINavigationItem!
#IBOutlet var mainView: UIView!
#IBOutlet weak var quizStackView: UIStackView!
var barTitle: String?
//variables for location services
var locationManager = CLLocationManager()
var locationData: LocationModel?
//variables for communication with Mediator
var QAMatcher = QuestionAnswerMediator()
var questions : [QuestionData]?
var categoryType: CategoryType!
var timer = Timer()
//Change UI depending on answer type
var uiBuilder = AnswerViewBuilder()
override func viewDidLoad() {
super.viewDidLoad()
setup(categoryType)
navigationBar.title = barTitle
QAMatcher.delegate = self
QAMatcher.getCategory(categoryType)
}
#objc func didChooseAnswer(_ sender: UIButton){
QAMatcher.nextQuestion()
updateUI()
}
}
//MARK: - Setup & updating UI
extension QuizViewController: MediatorDelegate {
func didUpdate(mediator: QuestionAnswerMediator, _: [QuestionData]) {
self.updateUI()
}
func setup(_ category: CategoryType?) {
switch category {
case .shortterm:
print("No function yet")
case .longterm:
print("No function yet")
case .problemsolving:
print("No function yet")
case .orientation:
locationManager.delegate = self
checkLocationServiceSupport()
locationManager.requestWhenInUseAuthorization()
gecodeCurrentLocation()
default:
let message: StaticString = M.Errors.invalidCategoryError
os_log(message, log: OSLog.default, type: .error)
}
}
func updateUI(){
if let label = questionLabel {
label.text = QAMatcher.getQuestion()
}
if let type = AnswerType(rawValue: QAMatcher.getAnswerType()) {
addAnswerSection(type)
}
}
func addAnswerSection(_ type: AnswerType) {
if let stackView = quizStackView, let answerSection = uiBuilder.getAnswerSection(answerType: type, didChooseAnswer: self.didChooseAnswer(_:)) {
stackView.addArrangedSubview(answerSection)
}
}
}
//MARK: - CLLocationManagerDelegate
extension QuizViewController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
locationManager.stopUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error)
{
let message: StaticString = M.Errors.locationDetectionError
os_log(message, log: OSLog.default, type: .error)
}
func checkLocationServiceSupport () {
if CLLocationManager.significantLocationChangeMonitoringAvailable() != true {
let alert = UIAlertController(title: M.Alters.locationAlertTitle, message: M.Alters.locationAlert, preferredStyle: .actionSheet)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { _ in
NSLog("The user acknowledge that location services are not available.")
}))
self.present(alert, animated: true, completion: nil)
self.dismiss(animated: true, completion: nil)
}
}
func gecodeCurrentLocation() {
locationManager.requestLocation()
if let lastLocation = self.locationManager.location {
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(lastLocation) { (placemarks, error) in
if error == nil {
if let placemark = placemarks?.first {
let city = placemark.locality!
let country = placemark.country!
self.locationData = LocationModel(city: city,country: country)
}
}
else {
let message: StaticString = M.Errors.locationDecodingError
os_log(message, log: OSLog.default, type: .error)
}
}
}
}
}
//MARK: - UITextFieldDelegate
//extension CategoryViewController: UITextFieldDelegate {
//
// func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
// if textField.text != "" {
// return true
// }
// else {
// textField.placeholder = "Enter your answer here"
// return false
// }
// }
//
// func textFieldDidEndEditing(_ textField: UITextField) {
// }
//
// func textFieldShouldReturn(_ textField: UITextField) -> Bool {
// textField.endEditing(true)
// return true
// }
//}
MulitpleChoiceView
import UIKit
class MultipleChoiceView: UIStackView {
var button1 = UIButton()
var button2 = UIButton()
var button3 = UIButton()
var didChooseAnswer : ((UIButton)->())?
init(frame: CGRect, didChooseAnswer: #escaping (UIButton)->()) {
super.init(frame: frame)
self.didChooseAnswer = didChooseAnswer
self.addAnswerView()
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func addAnswerView() {
setupStackView()
costumeButtons()
}
func setupStackView() {
self.alignment = .fill
self.distribution = .fillEqually
self.spacing = 10
self.axis = .vertical
}
func costumeButtons() {
let buttons = [button1 , button2, button3]
let dimensions = CGRect(x: 0, y: 0, width: 120, height: 20)
let color = #colorLiteral(red: 0.7294117647, green: 0.7450980392, blue: 1, alpha: 1)
for button in buttons {
button.frame = dimensions
button.backgroundColor = color
button.layer.cornerRadius = button.frame.size.height / 4
button.isUserInteractionEnabled = true
button.addTarget(self, action: M.Selector.buttonAction, for: .touchUpInside)
self.addArrangedSubview(button)
}
}
#objc func didChooseAnswer(_ sender: UIButton) {
if let didChooseAnswer = didChooseAnswer {
didChooseAnswer(sender)
}
}
}
AnswerViewBuilder
import UIKit
class AnswerViewBuilder {
func getAnswerSection(answerType: AnswerType, didChooseAnswer: #escaping (UIButton)->()) -> UIView?{
switch (answerType) {
case .multipleChoice:
return MultipleChoiceView(frame: CGRect(x: 0, y: 0, width: 100, height: 100), didChooseAnswer: didChooseAnswer)
case .textField:
return TextFieldView()
case .polarQuestion:
return PolarQuestionView()
case .ranking:
return MultipleChoiceView(frame: CGRect(x: 0, y: 0, width: 100, height: 100), didChooseAnswer: didChooseAnswer)
default:
print("Answer section could not be created.")
return nil
}
}
}

How to move to previous TextField when delete button is clicked on built-in keyboard iOS

I am building an iOS app which requires user to input pin which consists of 4 digits. So, I created 4 TextField separately in order to accept one number for each TextField. It is working fine when user inputs each textfield and move forward from textfield to another textfield smoothly.
But the problem is that I want user be able to delete by clicking on clear button icon that provided by iOS built-in keyboard. When user clicks on that icon button it should let textfield move to previous textfield but it does not work for me.
I have found lots of resources online on stackoverflow and it is still not working for me. That is why I created my own question.
This is how my code looks likes! I created textfield programmatically!
self.coverView.addSubview(self.paymentView.firstDigit)
self.coverView.addSubview(self.paymentView.secondDigit)
self.coverView.addSubview(self.paymentView.thirdDigit)
self.coverView.addSubview(self.paymentView.fourthDigit)
self.view.addSubview(self.overlayView)
self.overlayView.snp.makeConstraints { (make) in
make.top.width.height.centerX.equalTo(self.coverView)
}
self.paymentView.firstDigit.becomeFirstResponder()
self.paymentView.firstDigit.snp.makeConstraints { (make) in
make.top.equalTo(self.coverView)
make.width.height.equalTo(21)
make.leading.equalTo(self.coverView)
}
self.paymentView.secondDigit.snp.makeConstraints { (make) in
make.top.equalTo(self.coverView)
make.leading.equalTo(self.paymentView.firstDigit.snp.trailing).offset(8)
make.width.height.equalTo(21)
}
self.paymentView.thirdDigit.snp.makeConstraints { (make) in
make.top.equalTo(self.coverView)
make.leading.equalTo(self.paymentView.secondDigit.snp.trailing).offset(8)
make.width.height.equalTo(21)
}
self.paymentView.fourthDigit.snp.makeConstraints { (make) in
make.top.equalTo(self.coverView)
make.leading.equalTo(self.paymentView.thirdDigit.snp.trailing).offset(8)
make.width.height.equalTo(21)
}
self.paymentView.firstDigit.delegate = self
self.paymentView.secondDigit.delegate = self
self.paymentView.thirdDigit.delegate = self
self.paymentView.fourthDigit.delegate = self
The code above included delegate of TextField. And below is how I setup target for each TextField
self.paymentView.firstDigit.addTarget(self, action: #selector(textfieldDidChange(textField:)), for: .editingChanged)
self.paymentView.secondDigit.addTarget(self, action: #selector(textfieldDidChange(textField:)), for: .editingChanged)
self.paymentView.thirdDigit.addTarget(self, action: #selector(textfieldDidChange(textField:)), for: .editingChanged)
self.paymentView.fourthDigit.addTarget(self, action: #selector(textfieldDidChange(textField:)), for: .editingChanged)
And below is the function of textfieldDidChange
#objc func textfieldDidChange(textField: UITextField) {
let text = textField.text
print("This is an amount of text ", text?.count)
if text?.count == 1 {
switch textField {
case self.paymentView.firstDigit:
self.paymentView.firstDigit.textColor = BaseColor.colorPrimary
self.paymentView.firstDigit.backgroundColor = BaseColor.colorPrimary
self.paymentView.secondDigit.becomeFirstResponder()
case self.paymentView.secondDigit:
self.paymentView.secondDigit.textColor = BaseColor.colorPrimary
self.paymentView.thirdDigit.becomeFirstResponder()
self.paymentView.secondDigit.backgroundColor = BaseColor.colorPrimary
case self.paymentView.thirdDigit:
self.paymentView.thirdDigit.textColor = BaseColor.colorPrimary
self.paymentView.fourthDigit.becomeFirstResponder()
self.paymentView.thirdDigit.backgroundColor = BaseColor.colorPrimary
case self.paymentView.fourthDigit:
self.paymentView.fourthDigit.textColor = BaseColor.colorPrimary
self.paymentView.fourthDigit.backgroundColor = BaseColor.colorPrimary
self.paymentView.fourthDigit.resignFirstResponder()
self.view.endEditing(true)
default:
break
}
}
if text?.count == 0 {
switch textField {
case self.paymentView.firstDigit:
self.paymentView.firstDigit.becomeFirstResponder()
self.paymentView.firstDigit.backgroundColor = .red
case self.paymentView.secondDigit:
self.paymentView.firstDigit.becomeFirstResponder()
self.paymentView.firstDigit.backgroundColor = .red
case self.paymentView.thirdDigit:
self.paymentView.secondDigit.becomeFirstResponder()
self.paymentView.secondDigit.backgroundColor = .red
case self.paymentView.fourthDigit:
self.paymentView.thirdDigit.becomeFirstResponder()
self.paymentView.thirdDigit.backgroundColor = .red
default:
break
}
}
}
The above is how I tried to make textfield move to previous textfield when clear button of built-in keyboard iOS is clicked but it did not move. And the code above I copied from the source How to move cursor from one text field to another automatically in swift ios programmatically?
I have worked with this just follow these steps and you will get results as per your need:
#IBOutlet weak var txt4: UITextField!
#IBOutlet weak var txt3: UITextField!
#IBOutlet weak var txt2: UITextField!
#IBOutlet weak var txt1: UITextField!
Add textField Delegate in view controller
In viewDidLoad assign delegate:
txt1.delegate = self
txt2.delegate = self
txt3.delegate = self
txt4.delegate = self
Then add this code you will get as you want
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if (textField == txt1) {
txt2.becomeFirstResponder()
}else if (textField == txt2) {
txt3.becomeFirstResponder()
}else if (textField == txt3) {
txt4.becomeFirstResponder()
}else if (textField == txt4) {
//here
var str = self.txt1.text! + self.txt2.text! + self.txt3.text! + self.txt4.text!
str = str.replacingOccurrences(of: " ", with: "")
self.verify(otp: str)
// self.navigationController?.pushViewController(vc, animated: true)
}
return true
}
func textFieldDidBeginEditing(_ textField: UITextField) {
if(textField.text?.count == 0)
{
textField.text = " "
}
}
#objc func setNextResponder(textfield : UITextField)
{
textfield.becomeFirstResponder()
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let _char = string.cString(using: .utf8)
// const char * _char = [string cStringUsingEncoding:NSUTF8StringEncoding];
// int isBackSpace = strcmp(_char, "\b");
let isBackSpace = strcmp(_char, "\\b")
if (isBackSpace == -92) {
// NSLog(#"Backspace was pressed");
if (textField == txt4)
{
if(textField.text!.count == 2)
{
}
else{
self.perform(#selector(setNextResponder), with: txt3, afterDelay: 0.01)
txt3.text = " "
}
}
else if (textField == txt3)
{
if(textField.text!.count == 2)
{
}
else{
self.perform(#selector(setNextResponder), with: txt2, afterDelay: 0.01)
txt2.text = " "
}
}
else if (textField == txt2)
{
if(textField.text!.count == 2)
{
}
else{
self.perform(#selector(setNextResponder), with: txt1, afterDelay: 0.01)
txt1.text = " "
}
}
else if (textField == txt1)
{
if(textField.text!.count == 2)
{
}
else{
textField.resignFirstResponder()
}
}
}
if (string.count > 1 && !(Scanner.init(string: string).scanInt(nil)))
{
return false;
}
let oldLength = textField.text!.count
let replacementLength = string.count
let rangeLength = range.length
let newLength = oldLength - rangeLength + replacementLength
// This 'tabs' to next field when entering digits
if (newLength == 2) {
if (textField == txt1)
{
self.perform(#selector(setNextResponder), with: txt2, afterDelay: 0.01)
}
else if (textField == txt2)
{
self.perform(#selector(setNextResponder), with: txt3, afterDelay: 0.01)
}
else if (textField == txt3)
{
self.perform(#selector(setNextResponder), with: txt4, afterDelay: 0.01)
}
else if(textField == txt4)
{
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
// let vc = self.storyboard?.instantiateViewController(withIdentifier: "navVC") as! NavigationViewController
// vc.modalPresentationStyle = .fullScreen
// self.present(vc, animated: true, completion: nil)
var str = self.txt1.text! + self.txt2.text! + self.txt3.text! + self.txt4.text!
str = str.replacingOccurrences(of: " ", with: "")
self.verify(otp: str)
}
}
}
return newLength <= 2;
}
My approach was like this:
import UIKit
class ViewController1: UIViewController, UITextFieldDelegate {
#IBOutlet weak var text1: UITextField!
#IBOutlet weak var text2: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
text1.delegate = self
text2.delegate = self
}
func textFieldShouldClear(_ textField: UITextField) -> Bool {
if textField == text1 {
textField.text = ""
textField.resignFirstResponder()
text2.becomeFirstResponder()
}
return false
}
}

How to improve a control statement for text fields in Swift / Xcode iOS App?

I'm setting up a new application for iPhone and I want to add users input information into my database only if the text field is not empty.
There are 4 text fields but just one has to be complete to update my database.
Swift and iOS coding are new to me.
Here the code I want to improve:
#IBAction func confirmButtonWasPressed(_ sender: UIButton) {
print("Redirection to confirmation screen.")
if confirmpasswordTextField.text != "" {
print("Updating informations.")
// Read values from text fields
let userName = newusernameTextField.text
let userMail = newmailTextField.text
let userPhone = newphoneTextField.text
let userPassword = newpasswordTextField.text
if userName?.isEmpty ?? true {
print("textField is empty")
} else {
print("textField has some text")
}
if userMail?.isEmpty ?? true {
print("textField is empty")
} else {
print("textField has some text")
}
if userPhone?.isEmpty ?? true {
print("textField is empty")
} else {
print("textField has some text")
}
if userPassword?.isEmpty ?? true {
print("textField is empty")
} else {
print("textField has some text")
}
}else{
print("Error: Please enter your password.")
}
}
I want to reduce this function and get input if there have some.
I can do it with this code but I believe there has a short way to do that.
Thanks.
extension String {
public var isBlank: Bool {
let trimmed = trimmingCharacters(in: .whitespacesAndNewlines)
return trimmed.isEmpty
}
}
#IBAction func confirmButtonWasPressed(_ sender: UIButton) {
validateLogin()
}
func validateLogin(){
guard let username = userNameTextfield.text, !username.isBlank else {
return
}
guard let password = passwordTextField.text, !password.isBlank else {
return
}
}
Just use the delegate and a small rewrite for the UITextField so you have the name it has in the Database. In whichever controller you are just do:
class TextField: UITextField {
var nameInDataBase: String
}
class MyViewController: UIViewController, UITextFieldDelegate {
#IBOutlet var newusernameTextField: TextField!
#IBOutlet var newmailTextField: TextField!
#IBOutlet var newphoneTextField: TextField!
#IBOutlet var newpasswordTextField: TextField!
func viewDidLoad(_ animated: Bool) {
super.viewDidLoad()
[newusernameTextField, newmailTextField, newphoneTextField, newpasswordTextField].forEach { $0.delegate = self }
newusernameTextField.nameInDataBase = "newUserName"
newmailTextField.nameInDataBase = "newMail"
/// etc.
}
func textFieldDidEndEditing(_ textField: UITextField) {
guard
let enteredText = textField.text,
let field = textField as? TextField
else {
print ("nothing added or not TextField field")
}
logInDataBase(field.nameInDataBase, enteredText)
}
If you need more info to log stuff in the database, it is just a matter of adding that info as variables of the child class of UITextField

Eureka - Row to present multiple controllers

I'd like to create Eureka row to look and behave as Postal Address Row in create/edit Contact Screen in iOS Contacts App. I need to present Label or Country Picker when corresponding button in cell is pressed. Based on Eureka documentation:
every row that displays a new view controller must conform to PresenterRowType protocol
However this protocol is generic. So my understanding is that I can't show more than one child screen per row. Do I get this right? Is it possible to present more than one child screen?
What I have so far follows.
Row Protocols:
protocol PostalAddressFormatterConformance: class {
var streetUseFormatterDuringInput: Bool { get set }
var streetFormatter: Formatter? { get set }
var stateUseFormatterDuringInput: Bool { get set }
var stateFormatter: Formatter? { get set }
var postalCodeUseFormatterDuringInput: Bool { get set }
var postalCodeFormatter: Formatter? { get set }
var cityUseFormatterDuringInput: Bool { get set }
var cityFormatter: Formatter? { get set }
}
protocol LabeledRowConformance {
func onLabelButtonDidPress()
}
protocol CountryRowConformance {
func onCountryButtonDidPress()
}
protocol PostalAddressRowConformance: PostalAddressFormatterConformance, LabeledRowConformance, CountryRowConformance {
var placeholderColor : UIColor? { get set }
var streetPlaceholder : String? { get set }
var statePlaceholder : String? { get set }
var postalCodePlaceholder : String? { get set }
var cityPlaceholder : String? { get set }
}
Postal Address Row Base class:
class _PostalAddressRow<Cell: CellType>: Row<Cell>, PostalAddressRowConformance, CountryRowConformance, LabeledRowConformance, KeyboardReturnHandler where Cell: BaseCell, Cell: PostalAddressCellConformance {
//MARK: - LabeledRowConformance
func onLabelButtonDidPress() {
// TODO: Present Label Picker Screen
}
//MARK: - CountryRowConformance
func onCountryButtonDidPress() {
// TODO: Present Country Picker Screen
}
//MARK: - KeyboardReturnHandler
/// Configuration for the keyboardReturnType of this row
var keyboardReturnType : KeyboardReturnTypeConfiguration?
//MARK: - PostalAddressRowConformance
/// The textColor for the textField's placeholder
var placeholderColor : UIColor?
/// The placeholder for the street textField
var streetPlaceholder : String?
/// The placeholder for the state textField
var statePlaceholder : String?
/// The placeholder for the zip textField
var postalCodePlaceholder : String?
/// The placeholder for the city textField
var cityPlaceholder : String?
/// A formatter to be used to format the user's input for street
var streetFormatter: Formatter?
/// A formatter to be used to format the user's input for state
var stateFormatter: Formatter?
/// A formatter to be used to format the user's input for zip
var postalCodeFormatter: Formatter?
/// A formatter to be used to format the user's input for city
var cityFormatter: Formatter?
/// If the formatter should be used while the user is editing the street.
var streetUseFormatterDuringInput: Bool
/// If the formatter should be used while the user is editing the state.
var stateUseFormatterDuringInput: Bool
/// If the formatter should be used while the user is editing the zip.
var postalCodeUseFormatterDuringInput: Bool
/// If the formatter should be used while the user is editing the city.
var cityUseFormatterDuringInput: Bool
public required init(tag: String?) {
streetUseFormatterDuringInput = false
stateUseFormatterDuringInput = false
postalCodeUseFormatterDuringInput = false
cityUseFormatterDuringInput = false
super.init(tag: tag)
}
}
Postal Address Row Final:
final class PostalAddressRow: _PostalAddressRow<PostalAddressCell>, RowType {
public required init(tag: String? = nil) {
super.init(tag: tag)
// TODO
cellProvider = CellProvider<PostalAddressCell>(nibName: "PostalAddressCell")
}
}
Cell:
public protocol CountryCellConformance {
var countryButton: UIButton? { get }
}
public protocol PostalAddressCellConformance: CountryCellConformance {
var streetTextField: UITextField? { get }
var stateTextField: UITextField? { get }
var postalCodeTextField: UITextField? { get }
var cityTextField: UITextField? { get }
}
class _PostalAddressCell<T: PostalAddressType>: Cell<T>, PostalAddressCellConformance, UITextFieldDelegate, CellType {
#IBOutlet weak var changeLabelButton: UIButton!
//MARK: - CountryCellConformance
#IBOutlet weak var countryButton: UIButton?
//MARK: - PostalAddressCellConformance
#IBOutlet weak var streetTextField: UITextField?
#IBOutlet weak var stateTextField: UITextField?
#IBOutlet weak var postalCodeTextField: UITextField?
#IBOutlet weak var cityTextField: UITextField?
// ??? Style Color
#IBOutlet var separatorViews: [UIView]!
// Helper
var textFieldOrdering: [UITextField?] = []
//MARK: - Lifecycle
public required init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
open override func awakeFromNib() {
super.awakeFromNib()
textFieldOrdering = [streetTextField, stateTextField, postalCodeTextField, cityTextField]
}
deinit {
streetTextField?.delegate = nil
streetTextField?.removeTarget(self, action: nil, for: .allEvents)
stateTextField?.delegate = nil
stateTextField?.removeTarget(self, action: nil, for: .allEvents)
postalCodeTextField?.delegate = nil
postalCodeTextField?.removeTarget(self, action: nil, for: .allEvents)
cityTextField?.delegate = nil
cityTextField?.removeTarget(self, action: nil, for: .allEvents)
}
//MARK: - Actions
#IBAction func changeLabelButtonPressed(_ sender: Any) {
if let rowConformance = row as? LabeledRowConformance {
rowConformance.onLabelButtonDidPress()
}
}
#IBAction func countryButtonPressed(_ sender: Any) {
if let rowConformance = row as? CountryRowConformance {
rowConformance.onCountryButtonDidPress()
}
}
func internalNavigationAction(_ sender: UIBarButtonItem) {
guard let inputAccesoryView = inputAccessoryView as? NavigationAccessoryView else { return }
var index = 0
for field in textFieldOrdering {
if field?.isFirstResponder == true {
let _ = sender == inputAccesoryView.previousButton ? textFieldOrdering[index-1]?.becomeFirstResponder() : textFieldOrdering[index+1]?.becomeFirstResponder()
break
}
index += 1
}
}
func textFieldDidChange(_ textField : UITextField){
if row.baseValue == nil{
row.baseValue = PostalAddress()
}
guard let textValue = textField.text else {
switch(textField) {
case let field where field == streetTextField:
row.value?.street = nil
case let field where field == stateTextField:
row.value?.state = nil
case let field where field == postalCodeTextField:
row.value?.postalCode = nil
case let field where field == cityTextField:
row.value?.city = nil
default:
break
}
return
}
if let rowConformance = row as? PostalAddressRowConformance {
var useFormatterDuringInput = false
var valueFormatter: Formatter?
switch(textField) {
case let field where field == streetTextField:
useFormatterDuringInput = rowConformance.streetUseFormatterDuringInput
valueFormatter = rowConformance.streetFormatter
case let field where field == stateTextField:
useFormatterDuringInput = rowConformance.stateUseFormatterDuringInput
valueFormatter = rowConformance.stateFormatter
case let field where field == postalCodeTextField:
useFormatterDuringInput = rowConformance.postalCodeUseFormatterDuringInput
valueFormatter = rowConformance.postalCodeFormatter
case let field where field == cityTextField:
useFormatterDuringInput = rowConformance.cityUseFormatterDuringInput
valueFormatter = rowConformance.cityFormatter
default:
break
}
if let formatter = valueFormatter, useFormatterDuringInput{
let value: AutoreleasingUnsafeMutablePointer<AnyObject?> = AutoreleasingUnsafeMutablePointer<AnyObject?>.init(UnsafeMutablePointer<T>.allocate(capacity: 1))
let errorDesc: AutoreleasingUnsafeMutablePointer<NSString?>? = nil
if formatter.getObjectValue(value, for: textValue, errorDescription: errorDesc) {
switch(textField){
case let field where field == streetTextField:
row.value?.street = value.pointee as? String
case let field where field == stateTextField:
row.value?.state = value.pointee as? String
case let field where field == postalCodeTextField:
row.value?.postalCode = value.pointee as? String
case let field where field == cityTextField:
row.value?.city = value.pointee as? String
default:
break
}
if var selStartPos = textField.selectedTextRange?.start {
let oldVal = textField.text
textField.text = row.displayValueFor?(row.value)
if let f = formatter as? FormatterProtocol {
selStartPos = f.getNewPosition(forPosition: selStartPos, inTextInput: textField, oldValue: oldVal, newValue: textField.text)
}
textField.selectedTextRange = textField.textRange(from: selStartPos, to: selStartPos)
}
return
}
}
}
guard !textValue.isEmpty else {
switch(textField){
case let field where field == streetTextField:
row.value?.street = nil
case let field where field == stateTextField:
row.value?.state = nil
case let field where field == postalCodeTextField:
row.value?.postalCode = nil
case let field where field == cityTextField:
row.value?.city = nil
default:
break
}
return
}
switch(textField){
case let field where field == streetTextField:
row.value?.street = textValue
case let field where field == stateTextField:
row.value?.state = textValue
case let field where field == postalCodeTextField:
row.value?.postalCode = textValue
case let field where field == cityTextField:
row.value?.city = textValue
default:
break
}
}
//MARK: - Setup
override func setup() {
super.setup()
height = { 149 }
selectionStyle = .none
for textField in textFieldOrdering {
textField?.addTarget(self,
action: #selector(_PostalAddressCell.textFieldDidChange(_:)), // TODO: Move in extension
for: .editingChanged)
textField?.textAlignment = .left
textField?.clearButtonMode = .whileEditing
textField?.delegate = self
textField?.font = .preferredFont(forTextStyle: .body)
}
for separator in separatorViews {
separator.backgroundColor = .gray
}
}
//MARK: - Update
override func update() {
super.update()
textLabel?.text = nil
detailTextLabel?.text = nil
imageView?.image = nil
for textField in textFieldOrdering {
textField?.isEnabled = !row.isDisabled
textField?.textColor = row.isDisabled ? .gray : .black
textField?.autocorrectionType = .no
textField?.autocapitalizationType = .words
}
streetTextField?.text = row.value?.street
streetTextField?.keyboardType = .asciiCapable
stateTextField?.text = row.value?.state
stateTextField?.keyboardType = .asciiCapable
postalCodeTextField?.text = row.value?.postalCode
postalCodeTextField?.keyboardType = .numbersAndPunctuation
cityTextField?.text = row.value?.city
cityTextField?.keyboardType = .asciiCapable
if let rowConformance = row as? PostalAddressRowConformance {
setPlaceholderToTextField(textField: streetTextField, placeholder: rowConformance.streetPlaceholder)
setPlaceholderToTextField(textField: stateTextField, placeholder: rowConformance.statePlaceholder)
setPlaceholderToTextField(textField: postalCodeTextField, placeholder: rowConformance.postalCodePlaceholder)
setPlaceholderToTextField(textField: cityTextField, placeholder: rowConformance.cityPlaceholder)
}
countryButton?.setTitle(String(describing: row.value?.country), for: .normal)
}
//MARK: - BaseCell Responder
override func cellCanBecomeFirstResponder() -> Bool {
return !row.isDisabled && (
streetTextField?.canBecomeFirstResponder == true ||
stateTextField?.canBecomeFirstResponder == true ||
postalCodeTextField?.canBecomeFirstResponder == true ||
cityTextField?.canBecomeFirstResponder == true
)
}
override func cellBecomeFirstResponder(withDirection direction: Direction) -> Bool {
return direction == .down ? textFieldOrdering.first??.becomeFirstResponder() ?? false : textFieldOrdering.last??.becomeFirstResponder() ?? false
}
override func cellResignFirstResponder() -> Bool {
return streetTextField?.resignFirstResponder() ?? true
&& stateTextField?.resignFirstResponder() ?? true
&& postalCodeTextField?.resignFirstResponder() ?? true
&& stateTextField?.resignFirstResponder() ?? true
&& cityTextField?.resignFirstResponder() ?? true
}
override var inputAccessoryView: UIView? {
if let v = formViewController()?.inputAccessoryView(for: row) as? NavigationAccessoryView {
guard let first = textFieldOrdering.first, let last = textFieldOrdering.last, first != last else { return v }
if first?.isFirstResponder == true {
v.nextButton.isEnabled = true
v.nextButton.target = self
v.nextButton.action = #selector(_PostalAddressCell.internalNavigationAction(_:)) // TODO: Move in extension
} else if last?.isFirstResponder == true {
v.previousButton.target = self
v.previousButton.action = #selector(_PostalAddressCell.internalNavigationAction(_:))
v.previousButton.isEnabled = true
} else {
v.previousButton.target = self
v.previousButton.action = #selector(_PostalAddressCell.internalNavigationAction(_:))
v.nextButton.target = self
v.nextButton.action = #selector(_PostalAddressCell.internalNavigationAction(_:))
v.previousButton.isEnabled = true
v.nextButton.isEnabled = true
}
return v
}
return super.inputAccessoryView
}
//MARK: - UITextFieldDelegate
func textFieldDidBeginEditing(_ textField: UITextField) {
formViewController()?.beginEditing(of: self)
formViewController()?.textInputDidBeginEditing(textField, cell: self)
}
func textFieldDidEndEditing(_ textField: UITextField) {
formViewController()?.endEditing(of: self)
formViewController()?.textInputDidEndEditing(textField, cell: self)
textFieldDidChange(textField)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
return formViewController()?.textInputShouldReturn(textField, cell: self) ?? true
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
return formViewController()?.textInputShouldEndEditing(textField, cell: self) ?? true
}
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
return formViewController()?.textInputShouldBeginEditing(textField, cell: self) ?? true
}
func textFieldShouldClear(_ textField: UITextField) -> Bool {
return formViewController()?.textInputShouldClear(textField, cell: self) ?? true
}
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
return formViewController()?.textInputShouldEndEditing(textField, cell: self) ?? true
}
//MARK: - Private
private func setPlaceholderToTextField(textField: UITextField?, placeholder: String?) {
if let placeholder = placeholder, let textField = textField {
if let color = (row as? PostalAddressRowConformance)?.placeholderColor {
textField.attributedPlaceholder = NSAttributedString(string: placeholder, attributes: [NSForegroundColorAttributeName: color])
} else {
textField.placeholder = placeholder
}
}
}
}
final class PostalAddressCell: _PostalAddressCell<PostalAddress> {
public required init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
Models:
protocol CountryType: Equatable {
var country: Country? { get set }
}
func == <T: CountryType>(lhs: T, rhs: T) -> Bool {
return lhs.country == rhs.country
}
//
protocol PostalAddressType: CountryType {
var street: String? { get set }
var state: String? { get set }
var postalCode: String? { get set }
var city: String? { get set }
}
func == <T: PostalAddressType>(lhs: T, rhs: T) -> Bool {
return lhs.street == rhs.street && lhs.state == rhs.state && lhs.postalCode == rhs.postalCode && lhs.city == rhs.city && lhs.country == rhs.country
}
//
class PostalAddress: PostalAddressType {
var street: String?
var state: String?
var postalCode: String?
var city: String?
var country: Country?
public init() {}
public init(street: String?, state: String?, postalCode: String?, city: String?, country: Country?) {
self.street = street
self.state = state
self.postalCode = postalCode
self.city = city
self.country = country
}
}

Resources