4

I have a simple UIViewController where I am trying to learn how an MDCTextField can be configured.

I have two errors

  1. the text field is not editable
  2. the helper text displays over the top of the text field instead of under it.

How do I fix these two errors? OR is there a better library for this kind of text field.

Here us the text field before being clicked on it. You can see the helper text is over the top of the text field.

enter image description here

Here is the text field after clicking on it. You can see that the placeholder text does float up and the placeholder text and underline do change color, but I still can not enter text and the helper text is still over the top instead of underneath.

enter image description here

Here is the sample code

let leftpad: CGFloat = 16

import UIKit
import MaterialComponents.MaterialTextFields

class ViewController: UIViewController, UIScrollViewDelegate {  // ,   UITextFieldDelegate

var containerHeight : CGFloat = 1000
let scrollView = UIScrollView()
var textFieldFloating : MDCTextField!
var textFieldControllerFloating = MDCTextInputControllerUnderline()

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    // Container
    containerView.delegate = self
    view.addSubview(containerView)
    containerView.contentSize = CGSize(width: view.frame.width, height: containerHeight)
    containerView.autoresizingMask = UIViewAutoresizing.flexibleBottomMargin
    containerView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
    containerView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true
    containerView.widthAnchor.constraint(equalTo: view.widthAnchor, constant: 0).isActive = true
    containerView.heightAnchor.constraint(equalTo: view.heightAnchor, constant: 0).isActive = true

    // Text
    textFieldFloating = MDCTextField()
    textFieldFloating.translatesAutoresizingMaskIntoConstraints = false
    // textFieldFloating.delegate = self
    textFieldFloating.placeholder = "Name"
    textFieldFloating.isEnabled = true
    textFieldFloating.isUserInteractionEnabled = true
    textFieldFloating.clearButtonMode = .whileEditing
    textFieldControllerFloating = MDCTextInputControllerUnderline(textInput: textFieldFloating)
    textFieldControllerFloating.helperText = "Enter a name"
    textFieldControllerFloating.leadingUnderlineLabelTextColor = UIColor.darkGray       // The helper text
    textFieldControllerFloating.trailingUnderlineLabelTextColor = UIColor.green
    textFieldControllerFloating.inlinePlaceholderColor = UIColor.lightGray              // inline label
    textFieldControllerFloating.borderFillColor = UIColor.white
    textFieldControllerFloating.isFloatingEnabled = true

    textFieldControllerFloating.activeColor = UIColor.orange                            // active label & underline
    textFieldControllerFloating.normalColor = UIColor.lightGray                         // default underline
    textFieldControllerFloating.errorColor = UIColor.red
    // textFieldControllerFloating.floatingPlaceholderNormalColor = UIColor.magenta

    containerView.addSubview(textFieldFloating)

}

override func viewDidLayoutSubviews() {

    // containerView.view.layout( textFieldFloating).center().left(leftpad).right(leftpad)
    textFieldFloating.topAnchor.constraint(equalTo: containerView.topAnchor, constant : 40 ).isActive = true
    textFieldFloating.leftAnchor.constraint(equalTo: containerView.leftAnchor, constant : leftpad ).isActive = true
    textFieldFloating.widthAnchor.constraint(equalTo: containerView.widthAnchor, constant : -leftpad * 2).isActive = true
    textFieldFloating.heightAnchor.constraint(equalToConstant: 40).isActive = true
}


override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}


// The container
let containerView: UIScrollView = {
    let view = UIScrollView()
    view.backgroundColor = UIColor.white
    view.translatesAutoresizingMaskIntoConstraints = false
    view.layer.masksToBounds = true                          // This makes the rounded corners visible
    let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleContainerTap))
    view.isUserInteractionEnabled = true
    view.addGestureRecognizer(tapGestureRecognizer)
    return view
}()
@objc func handleContainerTap() {
    resignFirstResponderListOnContainerTap()
}
func resignFirstResponderListOnContainerTap() {
    // tfName.resignFirstResponder()
}



}
Nitish
  • 13,845
  • 28
  • 135
  • 263
john
  • 505
  • 1
  • 7
  • 15
  • https://github.com/material-components/material-components-ios/issues/3295 – Shahbaz Akram Apr 18 '18 at 06:22
  • Take it step by step and set attributes as per this example - https://github.com/material-components/material-components-ios/blob/develop/components/TextFields/examples/TextFieldExample.swift – Nitish Apr 18 '18 at 06:57

2 Answers2

4

Try removing the height constraint of the MDCTextfield.I also got the same problem and got the fix after the removed the height constraint as MDCTexfield takes it's own height for proper functioning.

Shivani Bajaj
  • 996
  • 10
  • 23
0

After spending too much time trying to set up the MDCTextField for a simple textField, I decided to develop my own subclass with i) The animated placeholder in the upper edge, ii) the info Label and iii) the animated line in the bottom.

It is a subclass of the UITextField, so everything works as expected with the UITextFieldDelegate methods

Use textField.placehoderText = "Placeholder" and textfield.infolabel.text = "Warning" to set up the auxiliary texts. The animations are automatic.

passwordTextField = AGTextField()
passwordTextField.delegate = self
passwordTextField.autocapitalizationType = .none
passwordTextField.keyboardType = UIKeyboardType.default
passwordTextField.isSecureTextEntry = true
passwordTextField.placehoderText = "Password"
self.view.addSubview(passwordTextField)
passwordTextField.addAnchorsAndCenter(centerX: true, centerY: nil, width: width, height: 30, left: nil, top: 30, right: nil, bottom: nil, withAnchor: .top, relativeToView: emailTextField)

Attached the subclass and autolayout helpers

import UIKit
class AGTextField: UITextField {
    var animationDuration: TimeInterval = 0.3
    var placehoderText: String = "" {
        didSet {
            placeholderLabel.text = placehoderText
        }
    }
    var placeholderLabel: UILabel!
    var infoLabel: UILabel!
    var activeColor: UIColor = .gray {
        didSet {
            line.backgroundColor = activeColor
        }
    }
    private let placeholderColor = UIColor.gray
    private let infoColor = UIColor.red
    private var line: UIView!
    convenience init() {
        self.init(frame: CGRect.zero)
        self.tintColor = activeColor
        self.font = UIFont(name: Fonts.regular, size: 16)
        line = UIView()
        line.backgroundColor = activeColor
        self.addSubview(line)
        line.addAnchorsAndSize(width: nil, height: 1.5, left: 0, top: nil, right: 0, bottom: 0)
        placeholderLabel = UILabel()
        placeholderLabel.font = UIFont(name: Fonts.regular, size: 16)
        placeholderLabel.textColor = .lightGray
        self.addSubview(placeholderLabel)
        Timer.scheduledTimer(withTimeInterval: 0.01, repeats: false) { (nil) in
            self.placeholderLabel.frame = self.bounds
        }
        infoLabel = UILabel()
        infoLabel.font = UIFont(name: Fonts.regular, size: 12)
        infoLabel.textColor = .red
        infoLabel.isHidden = true
        self.addSubview(infoLabel)
        infoLabel.addAnchors(left: 0, top: 8, right: nil, bottom: nil, withAnchor: .top, relativeToView: line)
        NotificationCenter.default.addObserver(self, selector: #selector(textFieldDidBeginEditing(notification:)), name: NSNotification.Name.UITextFieldTextDidBeginEditing, object: self)
        NotificationCenter.default.addObserver(self, selector: #selector(textFieldDidEndEditing(notification:)), name: NSNotification.Name.UITextFieldTextDidEndEditing, object: self)
    }
    @objc func textFieldDidBeginEditing(notification: Notification) {
        UIView.animate(withDuration: animationDuration) {
            self.placeholderLabel.frame.origin.y = self.line.frame.origin.y - 50.0
            self.placeholderLabel.font = UIFont(name: Fonts.regular, size: 12)
            self.line.backgroundColor = .gray
        }
        line.backgroundColor = Colors.accent
        infoLabel.isHidden = true
    }
    @objc func textFieldDidEndEditing(notification: Notification) {
        if self.text?.count == 0 {
            UIView.animate(withDuration: animationDuration) {
                self.placeholderLabel.frame = self.bounds
                self.placeholderLabel.font = UIFont(name: Fonts.regular, size: 16)
            }
        }
        self.line.backgroundColor = .gray
    }
    func showInfo(_ text: String) {
        infoLabel.text = text
        infoLabel.isHidden = false
    }
    func hideInfo() {
        infoLabel.isHidden = true
    }
}

I am also adding my Autolayout UIView Extension

import UIKit
enum Anchor { case left, top, right, bottom }
extension UIView {
    /**
    Description: Centers the view in the superview, using the superview's **size** and **XYAxis** position, not the left, top, right, bottom anchors to avoid issues with the *UISCrollViews*
    Parameters: None
    */
    func addAnchorsCenterAndFillContainer() {
        self.translatesAutoresizingMaskIntoConstraints = false
        self.widthAnchor.constraint(equalTo: self.superview!.widthAnchor).isActive = true
        self.heightAnchor.constraint(equalTo: self.superview!.heightAnchor).isActive = true
        self.centerXAnchor.constraint(equalTo: self.superview!.centerXAnchor).isActive = true
        self.centerYAnchor.constraint(equalTo: self.superview!.centerYAnchor).isActive = true
    }

    /**
    Adds 2 optional alignment parameterts (**centerX**, **centerY**), 2 optional size dimensions (**width** and **height**) and up to 4 border anchors **.left**, **.top**, **.right** and **.bottom**. One of them (defined in **withAnchor** can be relative to another view

    - Parameter centerX: **Bool** value (or *nil*) to define if the view should be centered **horizontally** to the superview. (optional)
    - Parameter centerY: **Bool** value (or *nil*) to define if the view should be centered **vertically** to the superview. (optional)
    - Parameter width: The **width** of the view (optional)
    - Parameter width: The **width** of the view (optional)
    - Parameter height: The **height** of the view (optional)
    - Parameter left: The **left** margin to the superview
    - Parameter top: The **top** margin to the superview
    - Parameter right: The **right** margin to the superview. *Magniture adjusted to be positive for margins inside the view*
    - Parameter bottom: The **bottom** margin to the superview. *Magniture adjusted to be positive for margins inside the view*
    - Parameter withAnchor: The **Anchor** type that is relative to the **relativeToView** view. *This parameter can be omited*
    - Parameter relativeToView: The **UIView** object that is the reference for the **withAnchor** anchor. *This parameter can be omited*

    - Returns: None
    */
    func addAnchorsAndCenter(centerX: Bool?, centerY: Bool?, width: CGFloat?, height: CGFloat?, left: CGFloat?, top: CGFloat?, right: CGFloat?, bottom: CGFloat?, withAnchor: Anchor? = nil, relativeToView: UIView? = nil) {

        self.translatesAutoresizingMaskIntoConstraints = false
        if centerX != nil {
            if centerX! == true {
                self.centerXAnchor.constraint(equalTo: self.superview!.centerXAnchor).isActive = true
            }
        }
        if centerY != nil {
            if centerY! == true {
                self.centerYAnchor.constraint(equalTo: self.superview!.centerYAnchor).isActive = true
            }
        }

        self.addAnchorsAndSize(width: width, height: height, left: left, top: top, right: right, bottom: bottom, withAnchor: withAnchor, relativeToView: relativeToView)
    }

    /**
    Adds 2 optional size dimensions (**width** and **height**) and up to 4 border anchors **.left**, **.top**, **.right** and **.bottom**. One of them (defined in **withAnchor** can be relative to another view

    - Parameter width: The **width** of the view (optional)
    - Parameter height: The **height** of the view (optional)
    - Parameter left: The **left** margin to the superview
    - Parameter top: The **top** margin to the superview
    - Parameter right: The **right** margin to the superview. *Magniture adjusted to be positive for margins inside the view*
    - Parameter bottom: The **bottom** margin to the superview. *Magniture adjusted to be positive for margins inside the view*
    - Parameter withAnchor: The **Anchor** type that is relative to the **relativeToView** view. *This parameter can be omited*
    - Parameter relativeToView: The **UIView** object that is the reference for the **withAnchor** anchor. *This parameter can be omited*

    - Returns: None
    */
    func addAnchorsAndSize(width: CGFloat?, height: CGFloat?, left: CGFloat?, top: CGFloat?, right: CGFloat?, bottom: CGFloat?, withAnchor: Anchor? = nil, relativeToView: UIView? = nil) {

        self.translatesAutoresizingMaskIntoConstraints = false
        if width != nil {
            self.widthAnchor.constraint(equalToConstant: width!).isActive = true
        }
        if height != nil {
            self.heightAnchor.constraint(equalToConstant: height!).isActive = true
        }
        self.addAnchors(left: left, top: top, right: right, bottom: bottom, withAnchor: withAnchor, relativeToView: relativeToView)
    }

    /**
    Adds up to 4 border anchors **.left**, **.top**, **.right** and **.bottom**. One of them (defined in **withAnchor** can be relative to another view

    - Parameter left: The **left** margin to the superview
    - Parameter top: The **top** margin to the superview
    - Parameter right: The **right** margin to the superview. *Magniture adjusted to be positive for margins inside the view*
    - Parameter bottom: The **bottom** margin to the superview. *Magniture adjusted to be positive for margins inside the view*
    - Parameter withAnchor: The **Anchor** type that is relative to the **relativeToView** view. *This parameter can be omited*
    - Parameter relativeToView: The **UIView** object that is the reference for the **withAnchor** anchor. *This parameter can be omited*

    - Returns: None
    */
    func addAnchors(left: CGFloat?, top: CGFloat?, right: CGFloat?, bottom: CGFloat?, withAnchor: Anchor? = nil, relativeToView: UIView? = nil) {

        self.translatesAutoresizingMaskIntoConstraints = false
        let superView = self.superview!
        if withAnchor != nil && relativeToView != nil {
            /**
            * Anchors relative to oposite anchors of reference view
            **/
            switch withAnchor! {
            case .left:
                if left != nil {
                    self.leftAnchor.constraint(equalTo: relativeToView!.rightAnchor, constant: left!).isActive = true
                }
            case .top:
                if top != nil {
                    self.topAnchor.constraint(equalTo: relativeToView!.bottomAnchor, constant: top!).isActive = true
                }
            case .right:
                if right != nil {
                    self.rightAnchor.constraint(equalTo: relativeToView!.leftAnchor, constant: -right!).isActive = true
                }
            case .bottom:
                if bottom != nil {
                    self.bottomAnchor.constraint(equalTo: relativeToView!.topAnchor, constant: -bottom!).isActive = true
                }
            }
        }

        /**
        * Anchors relative to same anchors of superview
        **/
        if let _anchor = withAnchor {
            if _anchor == .left {
                if top != nil {
                    self.topAnchor.constraint(equalTo: superView.topAnchor, constant: top!).isActive = true
                }
                if right != nil {
                    self.rightAnchor.constraint(equalTo: superView.rightAnchor, constant: -right!).isActive = true
                }
                if bottom != nil {
                    self.bottomAnchor.constraint(equalTo: superView.bottomAnchor, constant: -bottom!).isActive = true
                }
            }
            if _anchor == .top {
                if left != nil {
                    self.leftAnchor.constraint(equalTo: superView.leftAnchor, constant: left!).isActive = true
                }
                if right != nil {
                    self.rightAnchor.constraint(equalTo: superView.rightAnchor, constant: -right!).isActive = true
                }
                if bottom != nil {
                    self.bottomAnchor.constraint(equalTo: superView.bottomAnchor, constant: -bottom!).isActive = true
                }
            }
            if _anchor == .right {
                if left != nil {
                    self.leftAnchor.constraint(equalTo: superView.leftAnchor, constant: left!).isActive = true
                }
                if top != nil {
                    self.topAnchor.constraint(equalTo: superView.topAnchor, constant: top!).isActive = true
                }
                if bottom != nil {
                    self.bottomAnchor.constraint(equalTo: superView.bottomAnchor, constant: -bottom!).isActive = true
                }
            }
            if _anchor == .bottom {
                if left != nil {
                    self.leftAnchor.constraint(equalTo: superView.leftAnchor, constant: (left!)).isActive = true
                }
                if top != nil {
                    self.topAnchor.constraint(equalTo: superView.topAnchor, constant: top!).isActive = true
                }
                if right != nil {
                    self.rightAnchor.constraint(equalTo: superView.rightAnchor, constant: -right!).isActive = true
                }
            }
        } else {
            if left != nil {
                self.leftAnchor.constraint(equalTo: superView.leftAnchor, constant: left!).isActive = true
            }
            if top != nil {
                self.topAnchor.constraint(equalTo: superView.topAnchor, constant: top!).isActive = true
            }
            if right != nil {
                self.rightAnchor.constraint(equalTo: superView.rightAnchor, constant: -right!).isActive = true
            }
            if bottom != nil {
                self.bottomAnchor.constraint(equalTo: superView.bottomAnchor, constant: -bottom!).isActive = true
            }
        }
    }
}
eharo2
  • 2,553
  • 1
  • 29
  • 39