2

I am pretty new to animating in Swift and have an idea of how to go about this but would like to see how others would do it -

I am trying to create the effect where the placeholder text of a search bar shrinks and moves up above the search field and changes to different color when user goes to enter search bar. Like this:

http://magicus.xyz when you click on username.

If I place a uilabel over a search bar and scale this label on entering the searcher Im not sure entering the search bar would register because a uilabel would be over it..

I am generally trying to replicate those textfields in general. How would I go about creating this animation? Or even adding a bottom border to my search bar?

Bridger:

//
//  Makestagram-Bridging-Header.h
//  round
//
//  Created by Skylar Thomas on 6/24/16.
//  Copyright © 2016 Make School. All rights reserved.
//

#ifndef Makestagram_Bridging_Header_h
#define Makestagram_Bridging_Header_h

#import "PureLayout.h"

#endif /* Makestagram_Bridging_Header_h */

enter image description here

ERROR:

enter image description here

My project:

enter image description here

blue
  • 7,175
  • 16
  • 81
  • 179
  • 1
    I think these are the effects you are after (hoshi, I believe it is called). https://github.com/raulriera/TextFieldEffects/tree/master/TextFieldEffects/TextFieldEffects – Paulo Jul 05 '16 at 20:33

2 Answers2

4

This is the source about a nice project that achieve exactly what your search:

    public enum EGFloatingTextFieldValidationType {
        case Email
        case Number
    }

    public class EGFloatingTextField: UITextField {


        private typealias EGFloatingTextFieldValidationBlock = ((text:String,inout message:String)-> Bool)!

        public var validationType : EGFloatingTextFieldValidationType!


        private var emailValidationBlock  : EGFloatingTextFieldValidationBlock
        private var numberValidationBlock : EGFloatingTextFieldValidationBlock


        let kDefaultInactiveColor = UIColor(white: CGFloat(0), alpha: CGFloat(0.54))
        let kDefaultActiveColor = UIColor.blueColor()
        let kDefaultErrorColor = UIColor.redColor()
        let kDefaultLineHeight = CGFloat(22)
        let kDefaultLabelTextColor = UIColor(white: CGFloat(0), alpha: CGFloat(0.54))


        public var floatingLabel : Bool!
        var label : UILabel!
        var labelFont : UIFont!
        var labelTextColor : UIColor!
        var activeBorder : UIView!
        var floating : Bool!
        var active : Bool!
        var hasError : Bool!
        var errorMessage : String!



        required public init(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            self.commonInit()
        }


        override public init(frame: CGRect) {
            super.init(frame: frame)
            self.commonInit()
        }

        func commonInit(){

            self.emailValidationBlock = ({(text:String, inout message: String) -> Bool in
                var emailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}"

                var emailTest = NSPredicate(format:"SELF MATCHES %@" , emailRegex)

                var  isValid = emailTest.evaluateWithObject(text)
                if !isValid {
                    message = "Invalid Email Address"
                }
                return isValid;
            })
            self.numberValidationBlock = ({(text:String,inout message: String) -> Bool in
                var numRegex = "[0-9.+-]+";
                var numTest = NSPredicate(format:"SELF MATCHES %@" , numRegex)

                var isValid =  numTest.evaluateWithObject(text)
                if !isValid {
                    message = "Invalid Number"
                }
                return isValid;

            })
            self.floating = false
            self.hasError = false

            self.labelTextColor = kDefaultLabelTextColor
            self.label = UILabel(frame: CGRectZero)
            self.label.font = self.labelFont
            self.label.textColor = self.labelTextColor
            self.label.textAlignment = NSTextAlignment.Left
            self.label.numberOfLines = 1
            self.label.layer.masksToBounds = false
            self.addSubview(self.label)


            self.activeBorder = UIView(frame: CGRectZero)
            self.activeBorder.backgroundColor = kDefaultActiveColor
            self.activeBorder.layer.opacity = 0
            self.addSubview(self.activeBorder)

            self.label.autoAlignAxis(ALAxis.Horizontal, toSameAxisOfView: self)
            self.label.autoPinEdge(ALEdge.Left, toEdge: ALEdge.Left, ofView: self)
            self.label.autoMatchDimension(ALDimension.Width, toDimension: ALDimension.Width, ofView: self)
            self.label.autoMatchDimension(ALDimension.Height, toDimension: ALDimension.Height, ofView: self)

            self.activeBorder.autoPinEdge(ALEdge.Bottom, toEdge: ALEdge.Bottom, ofView: self)
            self.activeBorder.autoPinEdge(ALEdge.Left, toEdge: ALEdge.Left, ofView: self)
            self.activeBorder.autoPinEdge(ALEdge.Right, toEdge: ALEdge.Right, ofView: self)
            self.activeBorder.autoSetDimension(ALDimension.Height, toSize: 2)

            NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("textDidChange:"), name: "UITextFieldTextDidChangeNotification", object: self)
        }
        public func setPlaceHolder(placeholder:String){
            self.label.text = placeholder
        }

        override public func becomeFirstResponder() -> Bool {

            var flag:Bool = super.becomeFirstResponder()

            if flag {

                if self.floatingLabel! {

                    if !self.floating! || self.text!.isEmpty {
                        self.floatLabelToTop()
                        self.floating = true
                    }
                } else {
                    self.label.textColor = kDefaultActiveColor
                    self.label.layer.opacity = 0
                }
                self.showActiveBorder()
            }

            self.active=flag
            return flag
        }
        override public func resignFirstResponder() -> Bool {

            var flag:Bool = super.becomeFirstResponder()

            if flag {

                if self.floatingLabel! {

                    if self.floating! && self.text!.isEmpty {
                        self.animateLabelBack()
                        self.floating = false
                    }
                } else {
                    if self.text!.isEmpty {
                        self.label.layer.opacity = 1
                    }
                }
                self.label.textColor = kDefaultInactiveColor
                self.showInactiveBorder()
                self.validate()
            }
            self.active = flag
            return flag

        }

        override public func drawRect(rect: CGRect){
            super.drawRect(rect)

            var borderColor = self.hasError! ? kDefaultErrorColor : kDefaultInactiveColor

            var textRect = self.textRectForBounds(rect)
            var context = UIGraphicsGetCurrentContext()
            var borderlines : [CGPoint] = [CGPointMake(0, CGRectGetHeight(textRect) - 1),
                CGPointMake(CGRectGetWidth(textRect), CGRectGetHeight(textRect) - 1)]

            if  self.enabled  {

                CGContextBeginPath(context);
                CGContextAddLines(context, borderlines, 2);
                CGContextSetLineWidth(context, 1.0);
                CGContextSetStrokeColorWithColor(context, borderColor.CGColor);
                CGContextStrokePath(context);

            } else {

                CGContextBeginPath(context);
                CGContextAddLines(context, borderlines, 2);
                CGContextSetLineWidth(context, 1.0);
                var  dashPattern : [CGFloat]  = [2, 4]
                CGContextSetLineDash(context, 0, dashPattern, 2);
                CGContextSetStrokeColorWithColor(context, borderColor.CGColor);
                CGContextStrokePath(context);

            }
        }

        func textDidChange(notif: NSNotification){
            self.validate()
        }

        func floatLabelToTop() {

            CATransaction.begin()
            CATransaction.setCompletionBlock { () -> Void in
                self.label.textColor = self.kDefaultActiveColor
            }

            var anim2 = CABasicAnimation(keyPath: "transform")
            var fromTransform = CATransform3DMakeScale(CGFloat(1.0), CGFloat(1.0), CGFloat(1))
            var toTransform = CATransform3DMakeScale(CGFloat(0.5), CGFloat(0.5), CGFloat(1))
            toTransform = CATransform3DTranslate(toTransform, -CGRectGetWidth(self.label.frame)/2, -CGRectGetHeight(self.label.frame), 0)
            anim2.fromValue = NSValue(CATransform3D: fromTransform)
            anim2.toValue = NSValue(CATransform3D: toTransform)
            anim2.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
            var animGroup = CAAnimationGroup()
            animGroup.animations = [anim2]
            animGroup.duration = 0.3
            animGroup.fillMode = kCAFillModeForwards;
            animGroup.removedOnCompletion = false;
            self.label.layer.addAnimation(animGroup, forKey: "_floatingLabel")
            self.clipsToBounds = false
            CATransaction.commit()
        }
        func showActiveBorder() {

            self.activeBorder.layer.transform = CATransform3DMakeScale(CGFloat(0.01), CGFloat(1.0), 1)
            self.activeBorder.layer.opacity = 1
            CATransaction.begin()
            self.activeBorder.layer.transform = CATransform3DMakeScale(CGFloat(0.01), CGFloat(1.0), 1)
            var anim2 = CABasicAnimation(keyPath: "transform")
            var fromTransform = CATransform3DMakeScale(CGFloat(0.01), CGFloat(1.0), 1)
            var toTransform = CATransform3DMakeScale(CGFloat(1.0), CGFloat(1.0), 1)
            anim2.fromValue = NSValue(CATransform3D: fromTransform)
            anim2.toValue = NSValue(CATransform3D: toTransform)
            anim2.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
            anim2.fillMode = kCAFillModeForwards
            anim2.removedOnCompletion = false
            self.activeBorder.layer.addAnimation(anim2, forKey: "_activeBorder")
            CATransaction.commit()
        }

        func animateLabelBack() {
            CATransaction.begin()
            CATransaction.setCompletionBlock { () -> Void in
                self.label.textColor = self.kDefaultInactiveColor
            }

            var anim2 = CABasicAnimation(keyPath: "transform")
            var fromTransform = CATransform3DMakeScale(0.5, 0.5, 1)
            fromTransform = CATransform3DTranslate(fromTransform, -CGRectGetWidth(self.label.frame)/2, -CGRectGetHeight(self.label.frame), 0);
            var toTransform = CATransform3DMakeScale(1.0, 1.0, 1)
            anim2.fromValue = NSValue(CATransform3D: fromTransform)
            anim2.toValue = NSValue(CATransform3D: toTransform)
            anim2.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)

            var animGroup = CAAnimationGroup()
            animGroup.animations = [anim2]
            animGroup.duration = 0.3
            animGroup.fillMode = kCAFillModeForwards;
            animGroup.removedOnCompletion = false;

            self.label.layer.addAnimation(animGroup, forKey: "_animateLabelBack")
            CATransaction.commit()
        }
        func showInactiveBorder() {

            CATransaction.begin()
            CATransaction.setCompletionBlock { () -> Void in
                self.activeBorder.layer.opacity = 0
            }
            var anim2 = CABasicAnimation(keyPath: "transform")
            var fromTransform = CATransform3DMakeScale(1.0, 1.0, 1)
            var toTransform = CATransform3DMakeScale(0.01, 1.0, 1)
            anim2.fromValue = NSValue(CATransform3D: fromTransform)
            anim2.toValue = NSValue(CATransform3D: toTransform)
            anim2.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
            anim2.fillMode = kCAFillModeForwards
            anim2.removedOnCompletion = false
            self.activeBorder.layer.addAnimation(anim2, forKey: "_activeBorder")
            CATransaction.commit()
        }

        func performValidation(isValid:Bool,message:String){
            if !isValid {
                self.hasError = true
                self.errorMessage = message
                self.labelTextColor = kDefaultErrorColor
                self.activeBorder.backgroundColor = kDefaultErrorColor
                self.setNeedsDisplay()
            } else {
                self.hasError = false
                self.errorMessage = nil
                self.labelTextColor = kDefaultActiveColor
                self.activeBorder.backgroundColor = kDefaultActiveColor
                self.setNeedsDisplay()
            }
        }

        func validate(){

            if self.validationType != nil {
                var message : String = ""

                if self.validationType! == .Email {

                    var isValid = self.emailValidationBlock(text: self.text, message: &message)

                    performValidation(isValid,message: message)

                } else {
                    var isValid = self.numberValidationBlock(text: self.text, message: &message)

                    performValidation(isValid,message: message)
                }
            }
        }


    }

    extension EGFloatingTextField {

    }

His name is EGFloatingTextField and you can find more details here

Usage:

let emailLabel = EGFloatingTextField(frame: CGRectMake(8, 64, CGRectGetWidth(self.view.bounds) - 16, 48))
emailLabel.floatingLabel = true
emailLabel.setPlaceHolder("Email")
emailLabel.validationType = .Email
emailLabel.keyboardType = .EmailAddress
self.view.addSubview(emailLabel)

P.S. You need to import PureLayout library, here you can find the full source and instruction to how to import it.

enter image description here

Alessandro Ornano
  • 34,887
  • 11
  • 106
  • 133
  • Having trouble implementing this - getting no such module with import PureLayout even though I wrote the required lines in my pod file. Should I add all this in a separate script? When I do that it doesn't recognize what an EG textfield is – blue Jul 04 '16 at 18:11
  • Looke here : https://github.com/PureLayout/PureLayout there is also the instruction to how to import it – Alessandro Ornano Jul 04 '16 at 18:12
  • Ok, I looked through that but it was a little vague - I don't get an error when I import it in a separate file, just when I import it in my view controller file. Do I put the above code in a separate file? – blue Jul 04 '16 at 18:20
  • Usually I've no problem with these manual instruction: https://github.com/PureLayout/PureLayout#manually-from-github – Alessandro Ornano Jul 04 '16 at 18:22
  • It says to run pod install but I don't know what that means - pod install what? Do I write pod install Purelayout? – blue Jul 04 '16 at 18:22
  • If you use pod, otherwise dont care about POD installation, follow my link about manual installation.. – Alessandro Ornano Jul 04 '16 at 18:23
  • Ok I am trying to follow manual install but still have the error - I downloaded the PureLayout folder (nested in the 2 other pure layout folders) and dragged this in my Xcode project. I already have a bridging header and within it I added the following - #import "PureLayout.h" – blue Jul 04 '16 at 18:26
  • Seems your are ok, try to clear the cache..or close and reopen project to indexing the new files – Alessandro Ornano Jul 04 '16 at 18:28
  • I tried that. I don't think Im doing this right. I put my bridging header code in the question.. I need help. – blue Jul 04 '16 at 18:33
  • Should I make a new bridging header? If so, how should I do this? – blue Jul 04 '16 at 18:34
  • Yes it's correct man, check if purelayout files are present in Build phases - Compiled sources – Alessandro Ornano Jul 04 '16 at 18:35
  • Absolutely dont need more than follow these 3 instruction: https://github.com/PureLayout/PureLayout#manually-from-github – Alessandro Ornano Jul 04 '16 at 18:37
  • They weren't before so I added them and my bridging header. Look at picture in my question. Sorry, I Still have the error after following your instructions – blue Jul 04 '16 at 18:40
  • Very strange, you can try to make a new test project and import PureLayout on a fresh situation – Alessandro Ornano Jul 04 '16 at 18:41
  • I just did, which means I wasn't doing it right. Im not installing it manually correctly. I don't think my bridging header is "hooked up" correctly or I am not placing the files in my project right because they don't show up until I app them under compiled sources. Do you just drag and drop? – blue Jul 04 '16 at 18:46
  • Just check that purelayout files are really copied inside your project with all the other files and not just the referenced symbolic links – Alessandro Ornano Jul 04 '16 at 18:50
  • They are. Ok when I dragged them all in again it configured a new bridging header for me.. it was blank. With just the import Purelayout.h still have error. How do I configure the header correctly? Think this is the issue – blue Jul 04 '16 at 18:56
  • Please help me. I can't figure this out. Could you attach a working project? – blue Jul 04 '16 at 18:57
  • Ok, but I did exactly what you said to- dragged those files in and added the import to my header. Still getting error. Do you know how to do it with pods? Or could you attach a working project please? I could really use this in my project and I can't get it. Please. – blue Jul 04 '16 at 19:04
  • You must try a new test project, than do in your project, otherwise you dont exit from this issue – Alessandro Ornano Jul 04 '16 at 19:07
  • I tried. I opened a new project and did all the same things but the files are 1. Not showing up in compiled sources 2. Still no module purelayout. Since you can get it to work would you please attach a working project? – blue Jul 04 '16 at 19:09
  • Now im not in front of my mac, im sorry – Alessandro Ornano Jul 04 '16 at 19:10
  • Will you please attach it later? – blue Jul 04 '16 at 19:13
  • Yes i will prepare it – Alessandro Ornano Jul 04 '16 at 19:22
  • Thanks. Do you have a working project with the text field implemented? – blue Jul 04 '16 at 19:37
  • It'd be great if you pointed out what files in your working project I would have to copy over to mine to get it to work – blue Jul 04 '16 at 23:37
  • Here you can download the testPL.zip (PureLayout in a Swift project) http://s000.tinyupload.com/index.php?file_id=08555162154443954925 – Alessandro Ornano Jul 05 '16 at 06:40
  • About the textfield you need only 1 library EGFloatingTextField.swift to build your project, see the examples in the official github repo. – Alessandro Ornano Jul 05 '16 at 06:55
  • Why can't I drag and drop the purelayout files from testPL into my project? That project works but I still can't make it work in my own project or a new project. – blue Jul 05 '16 at 12:06
  • These files are the same downloadable from the github official repo so you can use them from github. – Alessandro Ornano Jul 05 '16 at 12:08
  • No, Im sorry the testPL doesn't work. Does not work. Same error. Look at my question – blue Jul 05 '16 at 12:21
  • Can you attach a project WITH import pure layout WRITTEN and WORKING? – blue Jul 05 '16 at 12:22
  • Sir , in Swift 2.x there is not need to import external library, there is no need to write import PureLayout in your class..testPL work well, otherwise you dont see any PureLayout library properties or methods. – Alessandro Ornano Jul 05 '16 at 12:27
  • Please don't use uppercase words, it's offensive. Im here just to help you. – Alessandro Ornano Jul 05 '16 at 12:28
  • Im sorry, I really appreciate your help here. Just been at this for a while. Let me try again. Thanks for your patience – blue Jul 05 '16 at 12:30
  • Just trying to figure out how to get the egfloatingtextfield library in then – blue Jul 05 '16 at 12:32
  • I've make a little example, you can see "ALMargin.Bottom" is a property come from PureLayout. If testPL dont would see PureLayout, you'd have an error in this line , but this is not happened. – Alessandro Ornano Jul 05 '16 at 12:32
  • Thanks, I see that now. I just dragged all those same files (again) into my project with my bridging header set up. The only thing that is different is the color of my folder (its blue not yellow) and it can't recognize the Purelayout function see in question – blue Jul 05 '16 at 12:38
  • If you wait a minute, I give to you another testPL2 with egfloatingtextfield working, it's very simple. I think you have some problem in your personal project or maybe some thing can could try to solve, dont worry about it. – Alessandro Ornano Jul 05 '16 at 12:40
  • when you have blue folders dont use drag and drop, right button of your mouse to the point you want to import files and click "Add files to ..", click options button in the left bottom and correct to added folders to create groups and not folder references ;) – Alessandro Ornano Jul 05 '16 at 12:46
  • THANK YOU. It finally works. It was the folder references not actual groups. Thank you. – blue Jul 05 '16 at 12:50
  • Hey, sorry to bug you again but do you know how to make the keyboard go away with this textfield? ResignfirstResponder and end editing don't work with it since its a custom textfield – blue Jul 06 '16 at 13:05
  • Your question is the most frequent swift question in StackOverflow, try to navigate through this thread http://stackoverflow.com/questions/1126726/how-to-make-a-uitextfield-move-up-when-keyboard-is-present – Alessandro Ornano Jul 06 '16 at 13:11
1

You can try it this way:

To create the animation:

  1. Place the label over textfield and add tap gestureRecogniser to label. Scale the label to its position and also made the textfield as first responder when you tap the label.

To create the bottom border:

  1. Add a UIView/UIImageView below the textfield with a height of 1 or 2 pixels based on your requirements. And set the no border style for the textfield.

For changing the icons with colors, try the font-awesome-icons. By this way you just need to pass the string name to change the icon to colored-icon.

Teja Nandamuri
  • 11,045
  • 6
  • 57
  • 109