So I have a validation for my textfield, if the validation is true my button will be enabled and if the validation is false my button will be disable. The problem is the validation only runs after I click the done button in the keyboard.
I want the validation check letter by letter.
Here is my code:
func textFieldDidEndEditing(_ textField: UITextField) {
if (nomorTextField.text!.count >= 10) {
nextButton.isEnabled = true
nextButton.backgroundColor = #colorLiteral(red: 1, green: 0.4431372549, blue: 0.003921568627, alpha: 1)
if(nomorTextField.text!.count > 13) {
nextButton.isEnabled = false
nextButton.backgroundColor = #colorLiteral(red: 0.662745098, green: 0.662745098, blue: 0.662745098, alpha: 1)
}
else if emailTextFeild.text == "" {
nextButton.isEnabled = true
nextButton.backgroundColor = #colorLiteral(red: 1, green: 0.4431372549, blue: 0.003921568627, alpha: 1)
}
else if emailTextFeild.text?.isEmail == false {
nextButton.isEnabled = false
nextButton.backgroundColor = #colorLiteral(red: 0.662745098, green: 0.662745098, blue: 0.662745098, alpha: 1)
}
}
else {
nextButton.isEnabled = false
nextButton.backgroundColor = #colorLiteral(red: 0.662745098, green: 0.662745098, blue: 0.662745098, alpha: 1)
}
}
You can also connect it as an IBAction from storyboard (if you're using storyboards)
P.S. don't forget to change type to UITextField, and event to ValueChanged.
You should use a different delegate method -
textField(_:shouldChangeCharactersIn:replacementString:)
This method is called right before the characters are changed in the text field. You should always return true because in your case you are not looking to prevent text edits. However you do need to update the edited text field text with the new characters to make sure the count is correct.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// Casting String as NSString to be able to replace characters using NSRange easily.
guard let text = textField.text as NSString? else { return true }
// Get the length of the final text
let finalTextCount = text.replacingCharacters(in: range, with: string).count
// Setup the text count for the field depending on which one was edited
let nomorTextFieldCount = textField == nomorTextField ? finalTextCount :
nomorTextField.text!.count
if nomorTextFieldCount >= 10 {
nextButton.isEnabled = true
nextButton.backgroundColor = #colorLiteral(red: 1, green: 0.4431372549, blue: 0.003921568627, alpha: 1)
if nomorTextFieldCount > 13 {
nextButton.isEnabled = false
nextButton.backgroundColor = #colorLiteral(red: 0.662745098, green: 0.662745098, blue: 0.662745098, alpha: 1)
}else if emailTextFeild.text == "" {
nextButton.isEnabled = true
nextButton.backgroundColor = #colorLiteral(red: 1, green: 0.4431372549, blue: 0.003921568627, alpha: 1)
}else if emailTextFeild.text?.isEmail == false{
nextButton.isEnabled = false
nextButton.backgroundColor = #colorLiteral(red: 0.662745098, green: 0.662745098, blue: 0.662745098, alpha: 1)
}
}else{
nextButton.isEnabled = false
nextButton.backgroundColor = #colorLiteral(red: 0.662745098, green: 0.662745098, blue: 0.662745098, alpha: 1)
}
return true
}
Related
Hello everyone!) Need some help!)
I have password validation text field and right now I'm setting UI. In text field I have only four rules. I created validation line which is UIView which has width - 348. The width and color of UIView must change every time if we add one rule to text field. If we have four rules, we must divide validation line by four: 348 / 4 = 87, and create four colors for it like red, orange, yellow and green.
How can I make the UIView available to update the width and color every time I add or subtract one rule in text field, like one lowercased character, digits, one uppercased character, ect...?)
Text field with four rules:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let text = validationTextField.text else { return true }
guard let textRange = Range(range, in: text) else { return true }
let updatedText = text.replacingCharacters(in: textRange, with: string)
//Minimum eight characters
if updatedText.count >= 8 {
eightCharsLablel.text = "⎷ minimum of 8 characters."
eightCharsLablel.textColor = #colorLiteral(red: 0.3411764801, green: 0.6235294342, blue: 0.1686274558, alpha: 1)
textFieldValidLineView.frame.size.width = 0 + 87
} else {
eightCharsLablel.text = "– minimum of 8 characters."
eightCharsLablel.textColor = #colorLiteral(red: 0.2849253164, green: 0.1806431101, blue: 0.5, alpha: 1)
}
//Minimum one digit
if updatedText.range(of: #"\d+"#, options: .regularExpression) != nil {
oneDigitLablel.text = "⎷ minimum 1 digit."
oneDigitLablel.textColor = #colorLiteral(red: 0.3411764801, green: 0.6235294342, blue: 0.1686274558, alpha: 1)
textFieldValidLineView.frame.size.width = 0 + 87
} else {
oneDigitLablel.text = "– minimum 1 digit."
oneDigitLablel.textColor = #colorLiteral(red: 0.2849253164, green: 0.1806431101, blue: 0.5, alpha: 1)
}
//Minimum one lowercased
if updatedText.range(of: #".*[a-z]+.*"#, options: .regularExpression) != nil {
oneLowercasedLablel.text = "⎷ minimum 1 lowercased."
oneLowercasedLablel.textColor = #colorLiteral(red: 0.3411764801, green: 0.6235294342, blue: 0.1686274558, alpha: 1)
} else {
oneLowercasedLablel.text = "– minimum 1 lowercased."
oneLowercasedLablel.textColor = #colorLiteral(red: 0.2849253164, green: 0.1806431101, blue: 0.5, alpha: 1)
}
//Minimum one uppercased
if updatedText.range(of: #".*[A-Z]+.*"#, options: .regularExpression) != nil {
oneUppercasedLablel.text = "⎷ minimum 1 uppercased."
oneUppercasedLablel.textColor = #colorLiteral(red: 0.3411764801, green: 0.6235294342, blue: 0.1686274558, alpha: 1)
} else {
oneUppercasedLablel.text = "– minimum 1 uppercased."
oneUppercasedLablel.textColor = #colorLiteral(red: 0.2849253164, green: 0.1806431101, blue: 0.5, alpha: 1)
}
//No whitespaces
if updatedText.range(of: #"\s+"#, options: .regularExpression) != nil {
return false
}
return true
}
Thanks for every answer!)
One way to do this that avoids any size calculations is to use a Horizontal UIStackView for your "ValidationLine".
If we set the stack view .distribution = .fillEqually, and then add a view for each "rule", the layout will happen automatically:
Then, to "grow and color" the "line" we can set the background colors of the arranged subviews based on how many rules have been "met":
Here's a complete example you can try out:
class ViewController: UIViewController, UITextFieldDelegate {
let ruleColors: [UIColor] = [
.red, .orange, .yellow, .green,
]
let validationTextField = UITextField()
let eightCharsLablel = UILabel()
let oneDigitLablel = UILabel()
let oneLowercasedLablel = UILabel()
let oneUppercasedLablel = UILabel()
// horizontal Stack View
let validationLineStackView: UIStackView = {
let v = UIStackView()
v.axis = .horizontal
v.distribution = .fillEqually
v.backgroundColor = UIColor(white: 0.9, alpha: 1.0)
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
// add one view for each rule to the validationLineStackView
// we'll set the background colors when we satisfy rules
for _ in 0..<ruleColors.count {
let v = UIView()
v.backgroundColor = .clear
validationLineStackView.addArrangedSubview(v)
}
// put everything in a vertical stack view for this example
let stackView = UIStackView()
stackView.axis = .vertical
stackView.spacing = 8
stackView.addArrangedSubview(validationTextField)
stackView.addArrangedSubview(validationLineStackView)
stackView.addArrangedSubview(eightCharsLablel)
stackView.addArrangedSubview(oneDigitLablel)
stackView.addArrangedSubview(oneLowercasedLablel)
stackView.addArrangedSubview(oneUppercasedLablel)
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
// stack view Top/Leading/Trailing with 20-points "padding"
stackView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
stackView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
stackView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
// we'll use the intrinsic heights for the text field and all labels
// but we need to set the height of the validationLineStackView
validationLineStackView.heightAnchor.constraint(equalToConstant: 8.0),
])
// to make it easier to see the text field
validationTextField.borderStyle = .roundedRect
validationTextField.backgroundColor = .cyan
validationTextField.delegate = self
// initial update
updateRulesProgress("")
}
func updateRulesProgress(_ updatedText: String) {
var numRulesMet: Int = 0
//Minimum eight characters
if updatedText.count >= 8 {
eightCharsLablel.text = "⎷ minimum of 8 characters."
eightCharsLablel.textColor = #colorLiteral(red: 0.3411764801, green: 0.6235294342, blue: 0.1686274558, alpha: 1)
// increment our "met" rules counter
numRulesMet += 1
} else {
eightCharsLablel.text = "– minimum of 8 characters."
eightCharsLablel.textColor = #colorLiteral(red: 0.2849253164, green: 0.1806431101, blue: 0.5, alpha: 1)
}
//Minimum one digit
if updatedText.range(of: #"\d+"#, options: .regularExpression) != nil {
oneDigitLablel.text = "⎷ minimum 1 digit."
oneDigitLablel.textColor = #colorLiteral(red: 0.3411764801, green: 0.6235294342, blue: 0.1686274558, alpha: 1)
// increment our "met" rules counter
numRulesMet += 1
} else {
oneDigitLablel.text = "– minimum 1 digit."
oneDigitLablel.textColor = #colorLiteral(red: 0.2849253164, green: 0.1806431101, blue: 0.5, alpha: 1)
}
//Minimum one lowercased
if updatedText.range(of: #".*[a-z]+.*"#, options: .regularExpression) != nil {
oneLowercasedLablel.text = "⎷ minimum 1 lowercased."
oneLowercasedLablel.textColor = #colorLiteral(red: 0.3411764801, green: 0.6235294342, blue: 0.1686274558, alpha: 1)
// increment our "met" rules counter
numRulesMet += 1
} else {
oneLowercasedLablel.text = "– minimum 1 lowercased."
oneLowercasedLablel.textColor = #colorLiteral(red: 0.2849253164, green: 0.1806431101, blue: 0.5, alpha: 1)
}
//Minimum one uppercased
if updatedText.range(of: #".*[A-Z]+.*"#, options: .regularExpression) != nil {
oneUppercasedLablel.text = "⎷ minimum 1 uppercased."
oneUppercasedLablel.textColor = #colorLiteral(red: 0.3411764801, green: 0.6235294342, blue: 0.1686274558, alpha: 1)
// increment our "met" rules counter
numRulesMet += 1
} else {
oneUppercasedLablel.text = "– minimum 1 uppercased."
oneUppercasedLablel.textColor = #colorLiteral(red: 0.2849253164, green: 0.1806431101, blue: 0.5, alpha: 1)
}
// now update the background colors of the views in the validationLineStackView
for i in 0..<validationLineStackView.arrangedSubviews.count {
if i < numRulesMet {
validationLineStackView.arrangedSubviews[i].backgroundColor = ruleColors[numRulesMet - 1]
} else {
validationLineStackView.arrangedSubviews[i].backgroundColor = .clear
}
}
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let text = validationTextField.text else { return true }
guard let textRange = Range(range, in: text) else { return true }
let updatedText = text.replacingCharacters(in: textRange, with: string)
// make this the first IF case, since we'll return without allowing the
// text to change -- so no need to check anything else
//No whitespaces
if updatedText.range(of: #"\s+"#, options: .regularExpression) != nil {
return false
}
// move all the rule IFs to the updateRulesProgress function
updateRulesProgress(updatedText)
return true
}
}
#IBAction func firstButton(_ sender: UIButton) {
if textLabel.text == "Cat" {
textLabel.backgroundColor = .green
view.backgroundColor = .green
secondButtonOutlet.isHidden = false
firstButtonOutlet.isHidden = true
picker.isHidden = true
buttonBackOutlet.isHidden = false
} else {
secondButtonOutlet.isHidden = true
countMistakes += 1
view.backgroundColor = .systemRed
textLabel.backgroundColor = .systemRed
UIView.animate(withDuration: 0, delay: 1, options: .allowAnimatedContent, animations: {
() -> () in
self.view.backgroundColor = #colorLiteral(red: 0.2392156869, green: 0.6745098233, blue: 0.9686274529, alpha: 1)
self.textLabel.backgroundColor = #colorLiteral(red: 0.2392156869, green: 0.6745098233, blue: 0.9686274529, alpha: 1)
}, completion: nil)
}
}
I have this code. My goal is to make an animation of red screen blinking. Everything goes well until textLabel. It doesn't want to change its color at all. Can anyone help me how to fix it ? (Background color is changing, so the code should be correct in overall).
In my case, I am trying to creating multiple buttons. Here, each buttons placed on separate UIView. This buttons working like a single section based on selection Its title color and UIView color I am changing in each button acton method. Here, I need to create a common extension for all button title and UIView color change. Once, button click need to pass the value to extension or a function to change the colors for selection button. This is I am trying for reducing the code duplication and LOC.
NOTE: Below I posted only one button code but I have many button. I want to make it common class and pass the value to change the colors. How to achieve this?
First Button Action
#IBAction func firstButtonClick(_ sender: Any) {
self.onetimeView.backgroundColor = colorLiteral(red: 0.184337255, green: 0.683529412, blue: 0.976475882, alpha: 1)
self.dailyView.backgroundColor = colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
self.weeklyView.backgroundColor = colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
self.fiftydaysView.backgroundColor = colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
self.monthlyView.backgroundColor = colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
self.onetimeButton.setTitleColor(UIColor.selectedColor, for: .normal)
self.dailyButton.setTitleColor(UIColor.disabledColor, for: .normal)
self.weeklyButton.setTitleColor(UIColor.disabledColor, for: .normal)
self.fiftydaysButton.setTitleColor(UIColor.disabledColor, for: .normal)
self.monthlyButton.setTitleColor(UIColor.disabledColor, for: .normal)
}
#IBAction func secondButtonClick(_ sender: Any) {
self.onetimeView.backgroundColor = colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
self.dailyView.backgroundColor = colorLiteral(red: 0.184337255, green: 0.683529412, blue: 0.976475882, alpha: 1)
self.weeklyView.backgroundColor = colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
self.fiftydaysView.backgroundColor = colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
self.monthlyView.backgroundColor = colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
self.onetimeButton.setTitleColor(UIColor.disabledColor, for: .normal)
self.dailyButton.setTitleColor(UIColor.selectedColor, for: .normal)
self.weeklyButton.setTitleColor(UIColor.disabledColor, for: .normal)
self.fiftydaysButton.setTitleColor(UIColor.disabledColor, for: .normal)
self.monthlyButton.setTitleColor(UIColor.disabledColor, for: .normal)
}
extension UIColor {
static var selectedColor = UIColor.init(red: 47/255, green: 174/255, blue: 248/255, alpha: 1)
static var disabledColor = UIColor.init(red: 170/255, green: 170/255, blue: 170/255, alpha: 1)
}
You can create a subclass like:
class PrimaryButton: UIButton {
}
Store your buttons and views in some data structure and iterate over it. Create a function that will set selectedColor on views passed as an argument and disabledColor on the rest.
typealias Section = (UIButton, UIView)
let sections: [Section] = [
(button: onetimeButton, view: onetimeView),
(button: dailyButton, view: dailyView),
(button: weeklyButton, view: weeklyView),
(button: fiftydaysButton, view: fiftydaysView),
(button: monthlyButton, view: monthlyView),
]
func select(_ section: Section) {
sections.forEach { (section) in
section.0.setTitleColor(UIColor.disabledColor, for: .normal)
section.1.backgroundColor = UIColor.disabledColor
}
section.0.setTitleColor(UIColor.selectedColor, for: .normal)
section.1.backgroundColor = UIColor.selectedColor
}
// in UIButton click call
select((onetimeButton, onetimeView))
How can I save the state of my toggle button using userdefaults? Here is the code, please help me.
import UIKit
class LawFigure: UIViewController {
//Outlets:
#IBOutlet weak var likeButton: UIButton!
//Variables:
var isButtonOn = false
override func viewDidLoad() {
super.viewDidLoad()
customiseUI()
}
func customiseUI() {
likeButton.layer.borderWidth = 3.0
likeButton.layer.borderColor = UIColor(red: 0/255, green: 166/255, blue: 221/255, alpha: 1.0).cgColor
likeButton.layer.cornerRadius = 20.0
likeButton.clipsToBounds = true
}
#IBAction func followActionPressed(_ sender: Any) {
self.activateButton(bool: !isButtonOn)
}
func activateButton(bool: Bool) {
isButtonOn = bool
likeButton.backgroundColor = bool ? UIColor(red: 0/255, green: 166/255, blue: 221/255, alpha: 1.0) : .clear
likeButton.setTitle(bool ? "unlike": "like", for: .normal)
likeButton.setTitleColor(bool ? .white : UIColor(red: 0/255, green: 166/255, blue: 221/255, alpha: 1.0), for: .normal)
}
}
You can try every open of the app
likeButton.isEnabled = !(UserDefaults.standard.bool(forKey:"IsBtnEnabled"))
whenever button is clicked save it
UserDefaults.standard.set(state, forKey: "IsBtnEnabled")
var isButtonOn = UserDefaults.standard.bool(forKey: "isButtonOn")
override func viewDidLoad() {
super.viewDidLoad()
activateButton(bool: isButtonOn)
customiseUI()
}
func activateButton(bool: Bool) {
isButtonOn = bool
UserDefaults.standard.set(isButtonOn, forKey: "isButtonOn")
UserDefaults.standard.synchronize()
likeButton.backgroundColor = bool ? UIColor(red: 0/255, green: 166/255, blue: 221/255, alpha: 1.0) : .clear
likeButton.setTitle(bool ? "unlike": "like", for: .normal)
likeButton.setTitleColor(bool ? .white : UIColor(red: 0/255, green: 166/255, blue: 221/255, alpha: 1.0), for: .normal)
}
When I'm using that function, the button not correctly worked. When I tapped on button then the button background color will change and when we tap on another button, then the background color of both the button will change. Please tell me how to solve this bug.
#IBAction func btnNew(_ sender: Any)
{
if otlNewVisitor.isSelected == false
{
otlNewVisitor.isSelected == true
Button.buttonPressed(button: otlNewVisitor, boolResult: true, titleColor: UIColor.white, strImage: "icn-new-visitor-wht", bgColor: UIColor(red: 30/255, green: 104/255, blue: 140/255, alpha: 1), imgVW: imgNewVis)
//Entered SubmitDetails Screen
let submitVC = self.storyboard?.instantiateViewController(withIdentifier: "SubmitDetailsVC") as! SubmitDetailsVC
self.navigationController?.pushViewController(submitVC, animated: true)
}
else
{
otlNewVisitor.isSelected == false
//Button Colow did change
Button.buttonPressed(button: otlNewVisitor, boolResult: false, titleColor: UIColor(red: 30/255, green: 104/255, blue: 140/255, alpha: 1), strImage: "icn-new-visitor", bgColor: UIColor.white, imgVW: imgNewVis)
}
}
You can changed your code to my code. This is only happens when you set the selected value of button within the if-else condition. So, you want to remove that code. It works well.
#IBAction func btnNew(_ sender: Any)
{
if otlNewVisitor.isSelected == false
{
Button.buttonPressed(button: otlNewVisitor, boolResult: true, titleColor: UIColor.white, strImage: "icn-new-visitor-wht", bgColor: UIColor(red: 30/255, green: 104/255, blue: 140/255, alpha: 1), imgVW: imgNewVis)
//Entered SubmitDetails Screen
let submitVC = self.storyboard?.instantiateViewController(withIdentifier: "SubmitDetailsVC") as! SubmitDetailsVC
self.navigationController?.pushViewController(submitVC, animated: true)
}
else
{
//Button Colow did change
Button.buttonPressed(button: otlNewVisitor, boolResult: false, titleColor: UIColor(red: 30/255, green: 104/255, blue: 140/255, alpha: 1), strImage: "icn-new-visitor", bgColor: UIColor.white, imgVW: imgNewVis)
}
}