diff --git a/AGInputControls/Source/FloatingLabelTextField.swift b/AGInputControls/Source/FloatingLabelTextField.swift index 0e3b764..cfc52c5 100644 --- a/AGInputControls/Source/FloatingLabelTextField.swift +++ b/AGInputControls/Source/FloatingLabelTextField.swift @@ -82,6 +82,13 @@ open class FloatingLabelTextField : FormattingTextField { } } + /// shows in the placeholder the designation of a required field using " \*" with a color from errorColor. Default is `false` + public var isRequired: Bool = false { + didSet { + configureColors() + } + } + /// Colors placeholder label, bottom label and underline view in `tintColor` when textfield is focused. Default is `true` open var highlightsWhenActive = true @@ -143,6 +150,8 @@ open class FloatingLabelTextField : FormattingTextField { textPadding.top + (font!.lineHeight * floatingLabelScaleFactor + floatingLabelBottomPadding) } + private let requiredText: String = " *" + //MARK: - Init required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) @@ -211,8 +220,20 @@ open class FloatingLabelTextField : FormattingTextField { } private func configurePlaceholder() { - guard placeholder != nil else { return } - placeholderLabel.text = placeholder + guard let placeholder else { return } + if isRequired { + placeholderLabel.text = nil + let placeholderWithRequiredText = placeholder + requiredText + let mutableAttributedString = NSMutableAttributedString( + string: placeholderWithRequiredText, + attributes: [.foregroundColor: placeholderLabel.textColor]) + let range = (placeholderWithRequiredText as NSString).range(of: requiredText) + mutableAttributedString.addAttribute(.foregroundColor, value: errorTintColor, range: range) + placeholderLabel.attributedText = mutableAttributedString + } else { + placeholderLabel.attributedText = nil + placeholderLabel.text = placeholder + } placeholderLabel.sizeToFit() } @@ -232,6 +253,7 @@ open class FloatingLabelTextField : FormattingTextField { } placeholderLabel.textColor = color + configurePlaceholder() underlineView.backgroundColor = color bottomLabel.textColor = color } diff --git a/Examples/FloatingLabelViewController.swift b/Examples/FloatingLabelViewController.swift index 308cf57..61b4f76 100644 --- a/Examples/FloatingLabelViewController.swift +++ b/Examples/FloatingLabelViewController.swift @@ -25,6 +25,9 @@ class StackViewController: UIViewController { stackView.axis = .vertical stackView.spacing = 1 + let endEditingTap = UITapGestureRecognizer(target: self, action: #selector(tapView)) + view.addGestureRecognizer(endEditingTap) + NSLayoutConstraint.activate([ stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 16), stackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 16), @@ -41,6 +44,10 @@ class StackViewController: UIViewController { // stackView.alignment = .leading // } + @objc private func tapView() { + view.endEditing(true) + } + final class Entry: UIView { let title: String @@ -96,6 +103,7 @@ final class FloatingLabelViewController: StackViewController { let floatTextField = FloatingLabelTextField() let floatingFieldNoFormatting = FloatingLabelTextField() + let floatingFieldNoFormattingWithRequired = FloatingLabelTextField() let floatingPhoneTextField = FloatingLabelTextField() override func viewDidLoad() { @@ -120,6 +128,15 @@ final class FloatingLabelViewController: StackViewController { floatingFieldNoFormatting.leftView?.tintColor = .lightGray floatingFieldNoFormatting.leftViewMode = .always + floatingFieldNoFormattingWithRequired.backgroundColor = UIColor.systemGreen.withAlphaComponent(0.15) + floatingFieldNoFormattingWithRequired.bottomText = "Bottom text" + floatingFieldNoFormattingWithRequired.placeholder = "Floating placeholder" + floatingFieldNoFormattingWithRequired.isRequired = true + floatingFieldNoFormattingWithRequired.showUnderlineView = true + floatingFieldNoFormattingWithRequired.highlightsWhenActive = true + floatingFieldNoFormattingWithRequired.clearButtonMode = .whileEditing + floatingFieldNoFormattingWithRequired.addTarget(self, action: #selector(didChangeNoFormattingWithRequired), for: .editingChanged) + floatTextField.placeholder = "Card number" floatTextField.tintColor = .systemPurple floatTextField.backgroundColor = .white @@ -141,6 +158,8 @@ final class FloatingLabelViewController: StackViewController { stackView.addArrangedSubview(Entry(title: "No formatting", targetView: floatingFieldNoFormatting)) + stackView.addArrangedSubview(Entry(title: "No formatting with required", targetView: floatingFieldNoFormattingWithRequired)) + stackView.addArrangedSubview(Entry(title: "Regular formatting", targetView: floatTextField)) stackView.addArrangedSubview(Entry(title: "Phone floating textfield", targetView: floatingPhoneTextField)) @@ -154,6 +173,11 @@ final class FloatingLabelViewController: StackViewController { tf.isError = isError } + @objc private func didChangeNoFormattingWithRequired(textField: UITextField) { + guard let tf = textField as? FloatingLabelTextField else { return } + tf.isError = tf.text!.count > 5 + } + @objc func didTapClear() { floatingFieldNoFormatting.text = nil } diff --git a/README.md b/README.md index 391405e..40c294e 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ FloatingLabelTextField is a `UITextField` subclass which resembles [Material des floatTextField.formattingMask = "##/##" // Formatting mask floatTextField.placeholder = "Expires at" floatTextField.textPaddings = UIEdgeInsets(top: 0, left: 8, bottom: 0, right: 0) // Paddings for text and floating placeholder + floatTextField.isRequired = true // Placeholder displays the required field symbol floatTextField.highlightsWhenActive = true // Placeholder and underline view and bottom label are filled by tintColor when textfield is active floatTextField.errorTintColor = .systemRed