Home > Enterprise >  How to change UIView "backgroundColor" and resize it appending text in UITextField (valida
How to change UIView "backgroundColor" and resize it appending text in UITextField (valida

Time:09-17


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...?)

enter image description here


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!)

CodePudding user response:

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:

enter image description here

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":

enter image description here

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
        
    }
}
  • Related