PlaceHolder animates when start typing in TextField in iOS
I've found the solution. You can manage this type of animation using multiple labels, and show-hide those labels into textFieldDidBeginEditing
method.
If you want nice animation same as you describe into your question, then try once following third party repository for UITextField
.
- JVFloatLabeledTextField
- UIFloatLabelTextField
- FloatLabelFields
If you are looking for the UITextView
equivalent of this animation, please visit UIFloatLabelTextView repository.
For Swift 4.0 and 4.2
Check this library for floating textField
https://github.com/hasnine/iOSUtilitiesSource
Code:
enum placeholderDirection: String {
case placeholderUp = "up"
case placeholderDown = "down"
}
public class IuFloatingTextFiledPlaceHolder: UITextField {
var enableMaterialPlaceHolder : Bool = true
var placeholderAttributes = NSDictionary()
var lblPlaceHolder = UILabel()
var defaultFont = UIFont()
var difference: CGFloat = 22.0
var directionMaterial = placeholderDirection.placeholderUp
var isUnderLineAvailabe : Bool = true
override init(frame: CGRect) {
super.init(frame: frame)
Initialize ()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
Initialize ()
}
func Initialize(){
self.clipsToBounds = false
self.addTarget(self, action: #selector(IuFloatingTextFiledPlaceHolder.textFieldDidChange), for: .editingChanged)
self.EnableMaterialPlaceHolder(enableMaterialPlaceHolder: true)
if isUnderLineAvailabe {
let underLine = UIImageView()
underLine.backgroundColor = UIColor.init(red: 197/255.0, green: 197/255.0, blue: 197/255.0, alpha: 0.8)
// underLine.frame = CGRectMake(0, self.frame.size.height-1, self.frame.size.width, 1)
underLine.frame = CGRect(x: 0, y: self.frame.size.height-1, width : self.frame.size.width, height : 1)
underLine.clipsToBounds = true
self.addSubview(underLine)
}
defaultFont = self.font!
}
@IBInspectable var placeHolderColor: UIColor? = UIColor.lightGray {
didSet {
self.attributedPlaceholder = NSAttributedString(string: self.placeholder! as String ,
attributes:[NSAttributedString.Key.foregroundColor: placeHolderColor!])
}
}
override public var placeholder:String? {
didSet {
// NSLog("placeholder = \(placeholder)")
}
willSet {
let atts = [NSAttributedString.Key.foregroundColor.rawValue: UIColor.lightGray, NSAttributedString.Key.font: UIFont.labelFontSize] as! [NSAttributedString.Key : Any]
self.attributedPlaceholder = NSAttributedString(string: newValue!, attributes:atts)
self.EnableMaterialPlaceHolder(enableMaterialPlaceHolder: self.enableMaterialPlaceHolder)
}
}
override public var attributedText:NSAttributedString? {
didSet {
// NSLog("text = \(text)")
}
willSet {
if (self.placeholder != nil) && (self.text != "")
{
let string = NSString(string : self.placeholder!)
self.placeholderText(string)
}
}
}
@objc func textFieldDidChange(){
if self.enableMaterialPlaceHolder {
if (self.text == nil) || (self.text?.count)! > 0 {
self.lblPlaceHolder.alpha = 1
self.attributedPlaceholder = nil
self.lblPlaceHolder.textColor = self.placeHolderColor
self.lblPlaceHolder.frame.origin.x = 0 ////\\
let fontSize = self.font!.pointSize;
self.lblPlaceHolder.font = UIFont.init(name: (self.font?.fontName)!, size: fontSize-3)
}
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.6, initialSpringVelocity: 1, options: .curveEaseInOut, animations: {() -> Void in
if (self.text == nil) || (self.text?.count)! <= 0 {
self.lblPlaceHolder.font = self.defaultFont
self.lblPlaceHolder.frame = CGRect(x: self.lblPlaceHolder.frame.origin.x+10, y : 0, width :self.frame.size.width, height : self.frame.size.height)
}
else {
if self.directionMaterial == placeholderDirection.placeholderUp {
self.lblPlaceHolder.frame = CGRect(x : self.lblPlaceHolder.frame.origin.x, y : -self.difference, width : self.frame.size.width, height : self.frame.size.height)
}else{
self.lblPlaceHolder.frame = CGRect(x : self.lblPlaceHolder.frame.origin.x, y : self.difference, width : self.frame.size.width, height : self.frame.size.height)
}
}
}, completion: {(finished: Bool) -> Void in
})
}
}
func EnableMaterialPlaceHolder(enableMaterialPlaceHolder: Bool){
self.enableMaterialPlaceHolder = enableMaterialPlaceHolder
self.lblPlaceHolder = UILabel()
self.lblPlaceHolder.frame = CGRect(x: 0, y : 0, width : 0, height :self.frame.size.height)
self.lblPlaceHolder.font = UIFont.systemFont(ofSize: 10)
self.lblPlaceHolder.alpha = 0
self.lblPlaceHolder.clipsToBounds = true
self.addSubview(self.lblPlaceHolder)
self.lblPlaceHolder.attributedText = self.attributedPlaceholder
//self.lblPlaceHolder.sizeToFit()
}
func placeholderText(_ placeholder: NSString){
let atts = [NSAttributedString.Key.foregroundColor: UIColor.lightGray, NSAttributedString.Key.font: UIFont.labelFontSize] as [NSAttributedString.Key : Any]
self.attributedPlaceholder = NSAttributedString(string: placeholder as String , attributes:atts)
self.EnableMaterialPlaceHolder(enableMaterialPlaceHolder: self.enableMaterialPlaceHolder)
}
override public func becomeFirstResponder()->(Bool){
let returnValue = super.becomeFirstResponder()
return returnValue
}
override public func resignFirstResponder()->(Bool){
let returnValue = super.resignFirstResponder()
return returnValue
}
override public func layoutSubviews() {
super.layoutSubviews()
}
}
This problem can be solved logically with the use of multiple labels and text-fields and later we can add animation if needed. I will like to explain this problem using three images, namely Img1, Img2 and Img3.
Img1 points to storyboard, where we have designed our interface. Here we have used three Labels each followed by TextField and UIView(line below Textfield).
Img2: It points to the initial screen when the app launches or when we press the "Sign up" Button at the bottom, which resets the screen. In this Image, the labels are hidden as textfields are blank with and view color is gray.
Img3: This image reflects the editing of Textfield. As we start editing text field(here the first one, namely name), the label shows up, size of textfield decreases, placeholder changes and color of view changes to black.
We need to keep one more thing in mind, when we stop editing any textfield and if it is still blank then set it properties to original.
I am adding code for this Question which I was asked as assignment in an interview.
import UIKit
class FloatingLabelViewController: UIViewController, UITextFieldDelegate, UINavigationControllerDelegate {
//UITextFieldDelegate - protocol defines methods that you use to manage the editing and validation of text in a UITextField object. All of the methods of this protocol are optional.
//UINavigationControllerDelegate - Use a navigation controller delegate (a custom object that implements this protocol) to modify behavior when a view controller is pushed or popped from the navigation stack of a UINavigationController object.
@IBOutlet weak var NameLbl: UILabel!
@IBOutlet weak var EmailLbl: UILabel!
@IBOutlet weak var PasswordLbl: UILabel!
@IBOutlet weak var NameTxf: UITextField!
@IBOutlet weak var EmailTxf: UITextField!
@IBOutlet weak var PasswordTxf: UITextField!
@IBOutlet weak var SignUpBtn: UIButton!
@IBOutlet weak var NameView: UIView!
@IBOutlet weak var EmailView: UIView!
@IBOutlet weak var PasswordView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
NameTxf.delegate = self
EmailTxf.delegate = self
PasswordTxf.delegate = self
self.property()
//black is varaiable here
//setTitleColor - Sets the color of the title to use for the specified state
//var layer - The view’s Core Animation layer used for rendering. this propert is never nil
//cg color - Quartz color refernce
SignUpBtn.backgroundColor = UIColor.black
SignUpBtn.setTitleColor(UIColor.white, for: .normal)
SignUpBtn.layer.borderWidth = 1
SignUpBtn.layer.borderColor = UIColor.black.cgColor
//Tap guesture recognizer to hide keyboard
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(FloatingLabelViewController.dismissKeyboard))
view.addGestureRecognizer(tap)
// UITapGestureRecognizer - UITapGestureRecognizer is a concrete subclass of UIGestureRecognizer that looks for single or multiple taps. For the gesture to be recognized, the specified number of fingers must tap the view a specified number of times.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
//textFieldShouldReturn - Asks the delegate if the text field should process the pressing of the return button. The text field calls this method whenever the user taps the return button. YES if the text field should implement its default behavior for the return button; otherwise, NO.
// endEditing - Causes the view (or one of its embedded text fields) to resign the first responder status.
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
self.view.endEditing(true)
return false
}
func dismissKeyboard() {
//Causes the view (or one of its embedded text fields) to resign the first responder status.
view.endEditing(true)
}
//When user Starts Editing the textfield
// textFieldDidBeginEditing - Tells the delegate that editing began in the specified text field
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField == self.NameTxf
{
self.NameTxf.font = UIFont.italicSystemFont(ofSize: 15)
self.NameLbl.isHidden = false
self.NameLbl.text = self.NameTxf.placeholder
self.NameTxf.placeholder = "First Last"
NameView.backgroundColor = UIColor.black.withAlphaComponent(0.5)
}
else if textField == self.EmailTxf
{
self.EmailTxf.font = UIFont.italicSystemFont(ofSize: 15)
self.EmailLbl.isHidden = false
self.EmailLbl.text = self.EmailTxf.placeholder
self.EmailTxf.placeholder = "[email protected]"
EmailView.backgroundColor = UIColor.black.withAlphaComponent(0.5)
}
else if textField == self.PasswordTxf
{
self.PasswordTxf.font = UIFont.italicSystemFont(ofSize: 15)
self.PasswordLbl.isHidden = false
self.PasswordLbl.text = self.PasswordTxf.placeholder
self.PasswordTxf.placeholder = "........."
PasswordView.backgroundColor = UIColor.black.withAlphaComponent(0.5)
}
}
//When User End editing the textfield.
// textFieldDidEndEditing - Tells the delegate that editing stopped for the specified text field.
func textFieldDidEndEditing(_ textField: UITextField) {
//Checkes if textfield is empty or not after after user ends editing.
if textField == self.NameTxf
{
if self.NameTxf.text == ""
{
self.NameTxf.font = UIFont.italicSystemFont(ofSize: 25)
self.NameLbl.isHidden = true
self.NameTxf.placeholder = "Name"
NameView.backgroundColor = UIColor.lightGray.withAlphaComponent(0.5)
}
}
else if textField == self.EmailTxf
{
if self.EmailTxf.text == ""
{
self.EmailTxf.font = UIFont.italicSystemFont(ofSize: 25)
self.EmailLbl.isHidden = true
self.EmailTxf.placeholder = "Email"
EmailView.backgroundColor = UIColor.lightGray.withAlphaComponent(0.5)
}
}
else if textField == self.PasswordTxf
{
if self.PasswordTxf.text == ""
{
self.PasswordTxf.font = UIFont.italicSystemFont(ofSize: 25)
self.PasswordLbl.isHidden = true
self.PasswordTxf.placeholder = "Password"
PasswordView.backgroundColor = UIColor.lightGray.withAlphaComponent(0.5)
}
}
}
//Action on SingUp button Clicked.
@IBAction func signupClicked(_ sender: Any) {
self.property()
self.dismissKeyboard() //TO dismiss the Keyboard on the click of SIGNUP button.
}
//Function to set the property of Textfields, Views corresponding to TextFields and Labels.
func property(){
NameLbl.isHidden = true
EmailLbl.isHidden = true
PasswordLbl.isHidden = true
NameTxf.text = ""
EmailTxf.text = ""
PasswordTxf.text = ""
NameTxf.placeholder = "Name"
EmailTxf.placeholder = "Email"
PasswordTxf.placeholder = "Password"
self.NameTxf.font = UIFont.italicSystemFont(ofSize: 25)
self.EmailTxf.font = UIFont.italicSystemFont(ofSize: 25)
self.PasswordTxf.font = UIFont.italicSystemFont(ofSize: 25)
EmailTxf.keyboardType = UIKeyboardType.emailAddress
PasswordTxf.isSecureTextEntry = true
NameTxf.autocorrectionType = .no
EmailTxf.autocorrectionType = .no
NameView.backgroundColor = UIColor.lightGray.withAlphaComponent(0.5)
EmailView.backgroundColor = UIColor.lightGray.withAlphaComponent(0.5)
PasswordView.backgroundColor = UIColor.lightGray.withAlphaComponent(0.5)
}
}