├── README.md ├── SDETextFieldExtension.swift ├── Screenshot ├── ColorDeleteAnimation.gif ├── DeleteAnimation.gif ├── InputAnimation.gif └── RockYou.gif └── TextFieldAnimationSample ├── Default-568h@2x.png ├── TextFieldAnimationSample.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── seedante.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── seedante.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── TextFieldAnimationSample.xcscheme │ └── xcschememanagement.plist └── TextFieldAnimationSample ├── AppDelegate.swift ├── Assets.xcassets ├── AppIcon.appiconset │ └── Contents.json ├── Contents.json ├── love.imageset │ ├── Contents.json │ ├── hearts-128.png │ ├── hearts-32.png │ └── hearts-64.png ├── node.imageset │ ├── Contents.json │ ├── node.png │ ├── node@2x.png │ └── node@3x.png ├── smallStar.imageset │ ├── Contents.json │ ├── smallStar.png │ └── smallStar@2x.png └── two_hearts.imageset │ ├── Contents.json │ ├── two_hearts-25.png │ ├── two_hearts-32.png │ └── two_hearts-50.png ├── Base.lproj ├── LaunchScreen.storyboard └── Main.storyboard ├── Info.plist ├── SDETextFieldExtension.swift └── ViewController.swift /README.md: -------------------------------------------------------------------------------- 1 | # TextFieldAnimationExtension 2 | 3 | 4 | Last week, @Joel Besada show an effect for [code edit](https://twitter.com/JoelBesada/status/670343885655293952). It's cool and useless. 5 | 6 | ![](https://cloud.githubusercontent.com/assets/688415/11440971/aadfae8e-9507-11e5-8aa0-0ecc87ca84b6.gif) 7 | 8 | What about this effect on iOS input? I think shake effect is not good for input, if you rock users, they will delete your app and give one star on app store. 9 | 10 | I explore some effects. Here is: 11 | 12 | Default effect: 13 | 14 | ![Delete Animation](https://github.com/seedante/TextFieldAnimationExtension/blob/master/Screenshot/DeleteAnimation.gif) 15 | 16 | Color Effect: 17 | 18 | ![Color Animation](https://github.com/seedante/TextFieldAnimationExtension/blob/master/Screenshot/ColorDeleteAnimation.gif) 19 | 20 | I don't think add effect on input is a good idea, but I still make a sample. You can custom input effect and in default it's disabled. 21 | 22 | ![Input Animtion](https://github.com/seedante/TextFieldAnimationExtension/blob/master/Screenshot/InputAnimation.gif) 23 | 24 | A ha, Rock you. Don't use this in your app. 25 | 26 | ![Rock You](https://github.com/seedante/TextFieldAnimationExtension/blob/master/Screenshot/RockYou.gif) 27 | 28 | 29 | ## Installation and Usage 30 | 31 | It's an extension! Drag **SDETextFieldExtension.swift** into your project. 32 | 33 | Enable effect: 34 | 35 | textField.addSDEEffect() 36 | 37 | Disable effect: 38 | 39 | textField.removeSDEEffect() 40 | 41 | Enable random color animation: 42 | 43 | textField.colorDeleteAnimationEnabled = true 44 | 45 | Input effect is disabled in default. You can enable and custom it: 46 | 47 | textField.inputAnimationEnabled = true 48 | textField.inputAnimationText = "❤️"//Accept the first letter 49 | 50 | In theory, UITextView can do same thing. I will implement it if I am not busy and in a good mood. 51 | -------------------------------------------------------------------------------- /SDETextFieldExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RockYouTextFieldExtension.swift 3 | // RockYouTextFieldSample 4 | // 5 | // Created by seedante on 15/12/2. 6 | // Copyright © 2015年 seedante. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | private var CurrentContextTextKey: UInt8 = 0 13 | private var ColorModeKey: UInt8 = 1 14 | private var InputAnimationEnabledKey: UInt8 = 2 15 | private var EmitterLayerKey: UInt8 = 3 16 | private var EmittingKey: UInt8 = 4 17 | private var InputAnimationTextKey: UInt8 = 5 18 | 19 | extension UITextField{ 20 | private var emitterLayer: CAEmitterLayer{ 21 | get { 22 | return objc_getAssociatedObject(self, &EmitterLayerKey) as! CAEmitterLayer 23 | } 24 | set(newValue) { 25 | objc_setAssociatedObject(self, &EmitterLayerKey, newValue, .OBJC_ASSOCIATION_RETAIN) 26 | } 27 | } 28 | 29 | private var currentContextText: String?{ 30 | get { 31 | return objc_getAssociatedObject(self, &CurrentContextTextKey) as? String 32 | } 33 | set(newValue) { 34 | objc_setAssociatedObject(self, &CurrentContextTextKey, newValue, .OBJC_ASSOCIATION_RETAIN) 35 | } 36 | } 37 | 38 | private var emitting: Bool{ 39 | get { 40 | return objc_getAssociatedObject(self, &EmittingKey) as! Bool 41 | } 42 | set(newValue) { 43 | objc_setAssociatedObject(self, &EmittingKey, newValue, .OBJC_ASSOCIATION_RETAIN) 44 | } 45 | } 46 | 47 | private var colors: [UIColor]{ 48 | return [UIColor.blackColor(), UIColor.darkGrayColor(), UIColor.lightGrayColor(), UIColor.whiteColor(), UIColor.grayColor(), UIColor.redColor(), UIColor.greenColor(), UIColor.blueColor(), 49 | UIColor.cyanColor(), UIColor.yellowColor(), UIColor.magentaColor(), UIColor.orangeColor(), UIColor.purpleColor(), UIColor.brownColor()] 50 | } 51 | 52 | //MARK: Custom Options 53 | //Text Color for deleted letter to throw: textfield's text color or random system color. 54 | var colorDeleteAnimationEnabled: Bool{ 55 | get { 56 | return objc_getAssociatedObject(self, &ColorModeKey) as! Bool 57 | } 58 | set(newValue) { 59 | objc_setAssociatedObject(self, &ColorModeKey, newValue, .OBJC_ASSOCIATION_RETAIN) 60 | } 61 | } 62 | 63 | //Input Effect Switch 64 | var inputAnimationEnabled: Bool{ 65 | get { 66 | return objc_getAssociatedObject(self, &InputAnimationEnabledKey) as! Bool 67 | } 68 | set(newValue) { 69 | objc_setAssociatedObject(self, &InputAnimationEnabledKey, newValue, .OBJC_ASSOCIATION_RETAIN) 70 | } 71 | } 72 | //Content of Input Animation 73 | var inputAnimationText: String?{ 74 | get { 75 | return objc_getAssociatedObject(self, &InputAnimationTextKey) as? String 76 | } 77 | set(newValue) { 78 | objc_setAssociatedObject(self, &InputAnimationTextKey, newValue, .OBJC_ASSOCIATION_RETAIN) 79 | } 80 | 81 | } 82 | 83 | //MARK: Config your textfield with one line code. 84 | func addSDEEffect(){ 85 | currentContextText = "" 86 | colorDeleteAnimationEnabled = false 87 | inputAnimationEnabled = false 88 | emitting = false 89 | setUpEmitterLayer() 90 | 91 | self.addTarget(self, action: "SDE_textDidChanged:", forControlEvents: .EditingChanged) 92 | } 93 | 94 | func removeSDEEffect(){ 95 | emitterLayer.removeFromSuperlayer() 96 | self.removeTarget(self, action: "SDE_textDidChanged:", forControlEvents: .EditingChanged) 97 | } 98 | 99 | //MARK: Input Animation 100 | func enableInputEffect(){ 101 | inputAnimationEnabled = true 102 | } 103 | 104 | func disableInputEffect(){ 105 | inputAnimationEnabled = false 106 | } 107 | 108 | private func setUpEmitterLayer() { 109 | clipsToBounds = false 110 | emitterLayer = CAEmitterLayer() 111 | emitterLayer.frame = bounds 112 | emitterLayer.seed = UInt32(NSDate().timeIntervalSince1970) 113 | layer.addSublayer(emitterLayer) 114 | } 115 | 116 | private func degreesToRadians(degrees: Double) -> CGFloat { 117 | return CGFloat(degrees * M_PI / 180.0) 118 | } 119 | 120 | private func imageOfText(drawText: NSString, textColor: UIColor)->UIImage{ 121 | UIGraphicsBeginImageContext(CGSize(width: 20, height: 20)) 122 | 123 | let textFont: UIFont = UIFont(name: "Helvetica Bold", size: 14)! 124 | let textFontAttributes = [ 125 | NSFontAttributeName: textFont, 126 | NSForegroundColorAttributeName: textColor, 127 | ] 128 | let rect: CGRect = CGRect(origin: CGPointZero, size: CGSize(width: 20, height: 20)) 129 | drawText.drawInRect(rect, withAttributes: textFontAttributes) 130 | let textImage: UIImage = UIGraphicsGetImageFromCurrentImageContext() 131 | 132 | UIGraphicsEndImageContext() 133 | return textImage 134 | 135 | } 136 | private func setUpEmitterCell() { 137 | let emitterCell = CAEmitterCell() 138 | 139 | if let emitterContentText = inputAnimationText{ 140 | emitterCell.contents = imageOfText(emitterContentText, textColor: textColor!).CGImage 141 | } 142 | 143 | emitterCell.enabled = true 144 | emitterCell.contentsRect = CGRect(origin: CGPointZero, size: CGSize(width: 1, height: 1)) 145 | emitterCell.color = UIColor.whiteColor().CGColor//UIColor(hue: 0.0, saturation: 0.0, brightness: 0.0, alpha: 1.0).CGColor 146 | emitterCell.redRange = 0.0 147 | emitterCell.greenRange = 0.0 148 | emitterCell.blueRange = 0.0 149 | emitterCell.alphaRange = 0.0 150 | emitterCell.redSpeed = 0.0 151 | emitterCell.greenSpeed = 0.0 152 | emitterCell.blueSpeed = 0.0 153 | emitterCell.alphaSpeed = -0.5 154 | emitterCell.scale = 1 155 | emitterCell.scaleRange = 0.1 156 | emitterCell.scaleSpeed = 0.1 157 | 158 | let zeroDegreesInRadians = degreesToRadians(0.0) 159 | emitterCell.spin = degreesToRadians(130.0) 160 | emitterCell.spinRange = zeroDegreesInRadians 161 | emitterCell.emissionLatitude = zeroDegreesInRadians 162 | emitterCell.emissionLongitude = zeroDegreesInRadians 163 | emitterCell.emissionRange = degreesToRadians(360.0) 164 | 165 | emitterCell.lifetime = 0.5 166 | emitterCell.lifetimeRange = 0.3 167 | emitterCell.birthRate = 50.0 168 | emitterCell.velocity = 50.0 169 | emitterCell.velocityRange = 500.0 170 | emitterCell.xAcceleration = -500.0 171 | emitterCell.yAcceleration = -500.0 172 | emitterLayer.emitterCells = [emitterCell] 173 | } 174 | 175 | //MARK: Shake Animation(Useless) 176 | func shakeX(){ 177 | let shakeXAnimation = CAKeyframeAnimation(keyPath: "position.x") 178 | shakeXAnimation.values = [0, 10, -10, 10, 0] 179 | shakeXAnimation.keyTimes = [0, (1 / 6.0), (3 / 6.0), (5 / 6.0), 1] 180 | shakeXAnimation.additive = true 181 | 182 | layer.addAnimation(shakeXAnimation, forKey: nil) 183 | } 184 | 185 | func shakeY(){ 186 | let shakeYAnimation = CAKeyframeAnimation(keyPath: "position.y") 187 | shakeYAnimation.values = [0, -10, 10, -10, 0] 188 | shakeYAnimation.keyTimes = [0, (1 / 6.0), (3 / 6.0), (5 / 6.0), 1] 189 | shakeYAnimation.additive = true 190 | 191 | layer.addAnimation(shakeYAnimation, forKey: nil) 192 | } 193 | 194 | //MARK: Helper 195 | @objc private func removeDiffView(diffView: UIView){ 196 | diffView.removeFromSuperview() 197 | } 198 | 199 | private func diffTextColor() -> UIColor{ 200 | var color: UIColor 201 | if colorDeleteAnimationEnabled{ 202 | let index = Int(UInt32(arc4random()) % UInt32(colors.count)) 203 | color = colors[index] 204 | }else{ 205 | color = textColor! 206 | } 207 | 208 | return color 209 | } 210 | 211 | @objc private func SDE_textDidChanged(textField: UITextField){ 212 | var cursorOrigin = caretRectForPosition(selectedTextRange!.start).origin 213 | 214 | let diffText = diffBetween(currentContextText!, newString: textField.text!) 215 | if currentContextText?.characters.count > text?.characters.count{ 216 | var startPoint = convertPoint(CGPoint(x: (cursorOrigin.x + frame.height), y: -frame.height / 4), toView: superview) 217 | if startPoint.x > frame.origin.x + frame.size.width{ 218 | startPoint.x = frame.origin.x + frame.size.width 219 | } 220 | 221 | let endPositionX = startPoint.x - 200 - CGFloat(UInt32(arc4random()) % UInt32(50)) 222 | let endPositionY = startPoint.y + 50 - CGFloat(UInt32(arc4random()) % UInt32(100)) 223 | let endPoint = CGPoint(x: endPositionX, y: endPositionY) 224 | let controlPoint = CGPoint(x: (startPoint.x + endPoint.x) / 2, y: startPoint.y - frame.height) 225 | 226 | let throwOrigin = CGPoint(x: (endPoint.x - frame.height / 2), y: (endPoint.y - frame.height / 2)) 227 | let viewSize = CGSize(width: frame.size.height, height: frame.size.height) 228 | let containerView = UIView(frame: CGRect(origin: throwOrigin, size: viewSize)) 229 | containerView.layer.opacity = 0 230 | 231 | //Maybe you want throw other things. 232 | let diffLabel = UILabel(frame: CGRect(origin: CGPointZero, size: viewSize)) 233 | diffLabel.font = font 234 | diffLabel.textColor = diffTextColor() 235 | diffLabel.adjustsFontSizeToFitWidth = true 236 | diffLabel.text = diffText 237 | 238 | containerView.addSubview(diffLabel) 239 | superview?.addSubview(containerView) 240 | 241 | let path = UIBezierPath() 242 | path.moveToPoint(startPoint) 243 | path.addQuadCurveToPoint(endPoint, controlPoint: controlPoint) 244 | let curvemoveAnimation = CAKeyframeAnimation(keyPath: "position") 245 | curvemoveAnimation.path = path.CGPath 246 | 247 | let rotateAnimation = CABasicAnimation(keyPath: "transform") 248 | let rotateRatio = CGFloat(UInt32(arc4random()) % UInt32(10)) / 10.0 249 | rotateAnimation.toValue = NSValue(CATransform3D: CATransform3DMakeRotation(CGFloat(M_PI_2), 0, 0, rotateRatio)) 250 | 251 | let fadeAnimation = CABasicAnimation(keyPath: "opacity") 252 | fadeAnimation.fromValue = 1.0 253 | fadeAnimation.toValue = 0.5 254 | 255 | let animationGroup = CAAnimationGroup() 256 | animationGroup.animations = [curvemoveAnimation, rotateAnimation, fadeAnimation] 257 | animationGroup.duration = 0.5 258 | 259 | containerView.layer.addAnimation(animationGroup, forKey: nil) 260 | performSelector("removeDiffView:", withObject: diffLabel, afterDelay: 0.51) 261 | }else if inputAnimationEnabled{ 262 | if !emitting{ 263 | setUpEmitterCell() 264 | emitting = true 265 | }else{ 266 | NSObject.cancelPreviousPerformRequestsWithTarget(self) 267 | } 268 | 269 | //Why 10 here, cursorOrigin returned by 'caretRectForPosition(selectedTextRange!.start).origin' is always not same with real position. 10 work fine with system font size from 14 to 30 most time. Calculation for accurate value is painful for me, if you know simple way, please tell me. 270 | if cursorOrigin.x > frame.size.width{ 271 | cursorOrigin.x = frame.size.width - 10 272 | }else{ 273 | cursorOrigin.x += 10 274 | } 275 | emitterLayer.emitterPosition = cursorOrigin 276 | performSelector("shutdownEmitterLayer", withObject: nil, afterDelay: 0.5) 277 | } 278 | 279 | currentContextText = text 280 | } 281 | 282 | @objc private func shutdownEmitterLayer(){ 283 | emitting = false 284 | emitterLayer.emitterCells = nil 285 | } 286 | 287 | private func diffBetween(oldString: String, newString: String) -> String{ 288 | if oldString.characters.count == 0{ 289 | return newString 290 | } 291 | 292 | if newString.characters.count == 0{ 293 | return oldString 294 | } 295 | 296 | var diffCharacter: Character = Character("X") 297 | let startIndex = oldString.startIndex 298 | let oldLastIndex = oldString.startIndex.advancedBy(oldString.characters.count - 1) 299 | let newLastIndex = newString.startIndex.advancedBy(newString.characters.count - 1) 300 | for (index, charactor) in oldString.characters.enumerate(){ 301 | let checkIndex = startIndex.advancedBy(index) 302 | let anotherCharacter = newString[checkIndex] 303 | 304 | if checkIndex == oldLastIndex{ 305 | if charactor == anotherCharacter{ 306 | //XY: XYZ 307 | diffCharacter = newString[newLastIndex] 308 | }else{ 309 | //XY: XZY 310 | diffCharacter = anotherCharacter 311 | } 312 | break 313 | } 314 | 315 | if checkIndex == newLastIndex{ 316 | if charactor == anotherCharacter{ 317 | //XYZ: XY 318 | diffCharacter = oldString[oldLastIndex] 319 | }else{ 320 | //XYZ: XZ 321 | diffCharacter = oldString[checkIndex] 322 | } 323 | break 324 | } 325 | 326 | if charactor != anotherCharacter{ 327 | if newString.characters.count > oldString.characters.count{ 328 | //OXYZ: OKXYZ 329 | diffCharacter = anotherCharacter 330 | }else{ 331 | //OXYZ: OYZ 332 | diffCharacter = charactor 333 | } 334 | break 335 | } 336 | } 337 | 338 | return String(diffCharacter) 339 | } 340 | 341 | } -------------------------------------------------------------------------------- /Screenshot/ColorDeleteAnimation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seedante/TextFieldAnimationExtension/b2d50c77c8a673c1dd6f94dd8e71c66c8c1515ef/Screenshot/ColorDeleteAnimation.gif -------------------------------------------------------------------------------- /Screenshot/DeleteAnimation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seedante/TextFieldAnimationExtension/b2d50c77c8a673c1dd6f94dd8e71c66c8c1515ef/Screenshot/DeleteAnimation.gif -------------------------------------------------------------------------------- /Screenshot/InputAnimation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seedante/TextFieldAnimationExtension/b2d50c77c8a673c1dd6f94dd8e71c66c8c1515ef/Screenshot/InputAnimation.gif -------------------------------------------------------------------------------- /Screenshot/RockYou.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seedante/TextFieldAnimationExtension/b2d50c77c8a673c1dd6f94dd8e71c66c8c1515ef/Screenshot/RockYou.gif -------------------------------------------------------------------------------- /TextFieldAnimationSample/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seedante/TextFieldAnimationExtension/b2d50c77c8a673c1dd6f94dd8e71c66c8c1515ef/TextFieldAnimationSample/Default-568h@2x.png -------------------------------------------------------------------------------- /TextFieldAnimationSample/TextFieldAnimationSample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | FDFA5F111C0D9FD00098837D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDFA5F101C0D9FD00098837D /* AppDelegate.swift */; }; 11 | FDFA5F131C0D9FD00098837D /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDFA5F121C0D9FD00098837D /* ViewController.swift */; }; 12 | FDFA5F161C0D9FD00098837D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FDFA5F141C0D9FD00098837D /* Main.storyboard */; }; 13 | FDFA5F181C0D9FD00098837D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = FDFA5F171C0D9FD00098837D /* Assets.xcassets */; }; 14 | FDFA5F1B1C0D9FD00098837D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FDFA5F191C0D9FD00098837D /* LaunchScreen.storyboard */; }; 15 | FDFA5F251C0EB1950098837D /* SDETextFieldExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDFA5F241C0EB1950098837D /* SDETextFieldExtension.swift */; }; 16 | FDFA5F271C118C450098837D /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FDFA5F261C118C450098837D /* Default-568h@2x.png */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | FDFA5F0D1C0D9FD00098837D /* TextFieldAnimationSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TextFieldAnimationSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 21 | FDFA5F101C0D9FD00098837D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 22 | FDFA5F121C0D9FD00098837D /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 23 | FDFA5F151C0D9FD00098837D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 24 | FDFA5F171C0D9FD00098837D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 25 | FDFA5F1A1C0D9FD00098837D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 26 | FDFA5F1C1C0D9FD00098837D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 27 | FDFA5F241C0EB1950098837D /* SDETextFieldExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SDETextFieldExtension.swift; sourceTree = ""; }; 28 | FDFA5F261C118C450098837D /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-568h@2x.png"; path = "../Default-568h@2x.png"; sourceTree = ""; }; 29 | /* End PBXFileReference section */ 30 | 31 | /* Begin PBXFrameworksBuildPhase section */ 32 | FDFA5F0A1C0D9FD00098837D /* Frameworks */ = { 33 | isa = PBXFrameworksBuildPhase; 34 | buildActionMask = 2147483647; 35 | files = ( 36 | ); 37 | runOnlyForDeploymentPostprocessing = 0; 38 | }; 39 | /* End PBXFrameworksBuildPhase section */ 40 | 41 | /* Begin PBXGroup section */ 42 | FDFA5F041C0D9FD00098837D = { 43 | isa = PBXGroup; 44 | children = ( 45 | FDFA5F0F1C0D9FD00098837D /* TextFieldAnimationSample */, 46 | FDFA5F0E1C0D9FD00098837D /* Products */, 47 | ); 48 | sourceTree = ""; 49 | }; 50 | FDFA5F0E1C0D9FD00098837D /* Products */ = { 51 | isa = PBXGroup; 52 | children = ( 53 | FDFA5F0D1C0D9FD00098837D /* TextFieldAnimationSample.app */, 54 | ); 55 | name = Products; 56 | sourceTree = ""; 57 | }; 58 | FDFA5F0F1C0D9FD00098837D /* TextFieldAnimationSample */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | FDFA5F101C0D9FD00098837D /* AppDelegate.swift */, 62 | FDFA5F121C0D9FD00098837D /* ViewController.swift */, 63 | FDFA5F241C0EB1950098837D /* SDETextFieldExtension.swift */, 64 | FDFA5F141C0D9FD00098837D /* Main.storyboard */, 65 | FDFA5F171C0D9FD00098837D /* Assets.xcassets */, 66 | FDFA5F191C0D9FD00098837D /* LaunchScreen.storyboard */, 67 | FDFA5F261C118C450098837D /* Default-568h@2x.png */, 68 | FDFA5F1C1C0D9FD00098837D /* Info.plist */, 69 | ); 70 | path = TextFieldAnimationSample; 71 | sourceTree = ""; 72 | }; 73 | /* End PBXGroup section */ 74 | 75 | /* Begin PBXNativeTarget section */ 76 | FDFA5F0C1C0D9FD00098837D /* TextFieldAnimationSample */ = { 77 | isa = PBXNativeTarget; 78 | buildConfigurationList = FDFA5F1F1C0D9FD00098837D /* Build configuration list for PBXNativeTarget "TextFieldAnimationSample" */; 79 | buildPhases = ( 80 | FDFA5F091C0D9FD00098837D /* Sources */, 81 | FDFA5F0A1C0D9FD00098837D /* Frameworks */, 82 | FDFA5F0B1C0D9FD00098837D /* Resources */, 83 | ); 84 | buildRules = ( 85 | ); 86 | dependencies = ( 87 | ); 88 | name = TextFieldAnimationSample; 89 | productName = RockYouTextFieldSample; 90 | productReference = FDFA5F0D1C0D9FD00098837D /* TextFieldAnimationSample.app */; 91 | productType = "com.apple.product-type.application"; 92 | }; 93 | /* End PBXNativeTarget section */ 94 | 95 | /* Begin PBXProject section */ 96 | FDFA5F051C0D9FD00098837D /* Project object */ = { 97 | isa = PBXProject; 98 | attributes = { 99 | LastSwiftUpdateCheck = 0710; 100 | LastUpgradeCheck = 0710; 101 | ORGANIZATIONNAME = seedante; 102 | TargetAttributes = { 103 | FDFA5F0C1C0D9FD00098837D = { 104 | CreatedOnToolsVersion = 7.1.1; 105 | }; 106 | }; 107 | }; 108 | buildConfigurationList = FDFA5F081C0D9FD00098837D /* Build configuration list for PBXProject "TextFieldAnimationSample" */; 109 | compatibilityVersion = "Xcode 3.2"; 110 | developmentRegion = English; 111 | hasScannedForEncodings = 0; 112 | knownRegions = ( 113 | en, 114 | Base, 115 | ); 116 | mainGroup = FDFA5F041C0D9FD00098837D; 117 | productRefGroup = FDFA5F0E1C0D9FD00098837D /* Products */; 118 | projectDirPath = ""; 119 | projectRoot = ""; 120 | targets = ( 121 | FDFA5F0C1C0D9FD00098837D /* TextFieldAnimationSample */, 122 | ); 123 | }; 124 | /* End PBXProject section */ 125 | 126 | /* Begin PBXResourcesBuildPhase section */ 127 | FDFA5F0B1C0D9FD00098837D /* Resources */ = { 128 | isa = PBXResourcesBuildPhase; 129 | buildActionMask = 2147483647; 130 | files = ( 131 | FDFA5F1B1C0D9FD00098837D /* LaunchScreen.storyboard in Resources */, 132 | FDFA5F271C118C450098837D /* Default-568h@2x.png in Resources */, 133 | FDFA5F181C0D9FD00098837D /* Assets.xcassets in Resources */, 134 | FDFA5F161C0D9FD00098837D /* Main.storyboard in Resources */, 135 | ); 136 | runOnlyForDeploymentPostprocessing = 0; 137 | }; 138 | /* End PBXResourcesBuildPhase section */ 139 | 140 | /* Begin PBXSourcesBuildPhase section */ 141 | FDFA5F091C0D9FD00098837D /* Sources */ = { 142 | isa = PBXSourcesBuildPhase; 143 | buildActionMask = 2147483647; 144 | files = ( 145 | FDFA5F251C0EB1950098837D /* SDETextFieldExtension.swift in Sources */, 146 | FDFA5F131C0D9FD00098837D /* ViewController.swift in Sources */, 147 | FDFA5F111C0D9FD00098837D /* AppDelegate.swift in Sources */, 148 | ); 149 | runOnlyForDeploymentPostprocessing = 0; 150 | }; 151 | /* End PBXSourcesBuildPhase section */ 152 | 153 | /* Begin PBXVariantGroup section */ 154 | FDFA5F141C0D9FD00098837D /* Main.storyboard */ = { 155 | isa = PBXVariantGroup; 156 | children = ( 157 | FDFA5F151C0D9FD00098837D /* Base */, 158 | ); 159 | name = Main.storyboard; 160 | sourceTree = ""; 161 | }; 162 | FDFA5F191C0D9FD00098837D /* LaunchScreen.storyboard */ = { 163 | isa = PBXVariantGroup; 164 | children = ( 165 | FDFA5F1A1C0D9FD00098837D /* Base */, 166 | ); 167 | name = LaunchScreen.storyboard; 168 | sourceTree = ""; 169 | }; 170 | /* End PBXVariantGroup section */ 171 | 172 | /* Begin XCBuildConfiguration section */ 173 | FDFA5F1D1C0D9FD00098837D /* Debug */ = { 174 | isa = XCBuildConfiguration; 175 | buildSettings = { 176 | ALWAYS_SEARCH_USER_PATHS = NO; 177 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 178 | CLANG_CXX_LIBRARY = "libc++"; 179 | CLANG_ENABLE_MODULES = YES; 180 | CLANG_ENABLE_OBJC_ARC = YES; 181 | CLANG_WARN_BOOL_CONVERSION = YES; 182 | CLANG_WARN_CONSTANT_CONVERSION = YES; 183 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 184 | CLANG_WARN_EMPTY_BODY = YES; 185 | CLANG_WARN_ENUM_CONVERSION = YES; 186 | CLANG_WARN_INT_CONVERSION = YES; 187 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 188 | CLANG_WARN_UNREACHABLE_CODE = YES; 189 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 190 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 191 | COPY_PHASE_STRIP = NO; 192 | DEBUG_INFORMATION_FORMAT = dwarf; 193 | ENABLE_STRICT_OBJC_MSGSEND = YES; 194 | ENABLE_TESTABILITY = YES; 195 | GCC_C_LANGUAGE_STANDARD = gnu99; 196 | GCC_DYNAMIC_NO_PIC = NO; 197 | GCC_NO_COMMON_BLOCKS = YES; 198 | GCC_OPTIMIZATION_LEVEL = 0; 199 | GCC_PREPROCESSOR_DEFINITIONS = ( 200 | "DEBUG=1", 201 | "$(inherited)", 202 | ); 203 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 204 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 205 | GCC_WARN_UNDECLARED_SELECTOR = YES; 206 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 207 | GCC_WARN_UNUSED_FUNCTION = YES; 208 | GCC_WARN_UNUSED_VARIABLE = YES; 209 | IPHONEOS_DEPLOYMENT_TARGET = 9.1; 210 | MTL_ENABLE_DEBUG_INFO = YES; 211 | ONLY_ACTIVE_ARCH = YES; 212 | SDKROOT = iphoneos; 213 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 214 | TARGETED_DEVICE_FAMILY = "1,2"; 215 | }; 216 | name = Debug; 217 | }; 218 | FDFA5F1E1C0D9FD00098837D /* Release */ = { 219 | isa = XCBuildConfiguration; 220 | buildSettings = { 221 | ALWAYS_SEARCH_USER_PATHS = NO; 222 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 223 | CLANG_CXX_LIBRARY = "libc++"; 224 | CLANG_ENABLE_MODULES = YES; 225 | CLANG_ENABLE_OBJC_ARC = YES; 226 | CLANG_WARN_BOOL_CONVERSION = YES; 227 | CLANG_WARN_CONSTANT_CONVERSION = YES; 228 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 229 | CLANG_WARN_EMPTY_BODY = YES; 230 | CLANG_WARN_ENUM_CONVERSION = YES; 231 | CLANG_WARN_INT_CONVERSION = YES; 232 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 233 | CLANG_WARN_UNREACHABLE_CODE = YES; 234 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 235 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 236 | COPY_PHASE_STRIP = NO; 237 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 238 | ENABLE_NS_ASSERTIONS = NO; 239 | ENABLE_STRICT_OBJC_MSGSEND = YES; 240 | GCC_C_LANGUAGE_STANDARD = gnu99; 241 | GCC_NO_COMMON_BLOCKS = YES; 242 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 243 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 244 | GCC_WARN_UNDECLARED_SELECTOR = YES; 245 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 246 | GCC_WARN_UNUSED_FUNCTION = YES; 247 | GCC_WARN_UNUSED_VARIABLE = YES; 248 | IPHONEOS_DEPLOYMENT_TARGET = 9.1; 249 | MTL_ENABLE_DEBUG_INFO = NO; 250 | SDKROOT = iphoneos; 251 | TARGETED_DEVICE_FAMILY = "1,2"; 252 | VALIDATE_PRODUCT = YES; 253 | }; 254 | name = Release; 255 | }; 256 | FDFA5F201C0D9FD00098837D /* Debug */ = { 257 | isa = XCBuildConfiguration; 258 | buildSettings = { 259 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 260 | INFOPLIST_FILE = TextFieldAnimationSample/Info.plist; 261 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 262 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 263 | PRODUCT_BUNDLE_IDENTIFIER = seedante.TextFieldAnimationSample; 264 | PRODUCT_NAME = TextFieldAnimationSample; 265 | }; 266 | name = Debug; 267 | }; 268 | FDFA5F211C0D9FD00098837D /* Release */ = { 269 | isa = XCBuildConfiguration; 270 | buildSettings = { 271 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 272 | INFOPLIST_FILE = TextFieldAnimationSample/Info.plist; 273 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 274 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 275 | PRODUCT_BUNDLE_IDENTIFIER = seedante.TextFieldAnimationSample; 276 | PRODUCT_NAME = TextFieldAnimationSample; 277 | }; 278 | name = Release; 279 | }; 280 | /* End XCBuildConfiguration section */ 281 | 282 | /* Begin XCConfigurationList section */ 283 | FDFA5F081C0D9FD00098837D /* Build configuration list for PBXProject "TextFieldAnimationSample" */ = { 284 | isa = XCConfigurationList; 285 | buildConfigurations = ( 286 | FDFA5F1D1C0D9FD00098837D /* Debug */, 287 | FDFA5F1E1C0D9FD00098837D /* Release */, 288 | ); 289 | defaultConfigurationIsVisible = 0; 290 | defaultConfigurationName = Release; 291 | }; 292 | FDFA5F1F1C0D9FD00098837D /* Build configuration list for PBXNativeTarget "TextFieldAnimationSample" */ = { 293 | isa = XCConfigurationList; 294 | buildConfigurations = ( 295 | FDFA5F201C0D9FD00098837D /* Debug */, 296 | FDFA5F211C0D9FD00098837D /* Release */, 297 | ); 298 | defaultConfigurationIsVisible = 0; 299 | defaultConfigurationName = Release; 300 | }; 301 | /* End XCConfigurationList section */ 302 | }; 303 | rootObject = FDFA5F051C0D9FD00098837D /* Project object */; 304 | } 305 | -------------------------------------------------------------------------------- /TextFieldAnimationSample/TextFieldAnimationSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /TextFieldAnimationSample/TextFieldAnimationSample.xcodeproj/project.xcworkspace/xcuserdata/seedante.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seedante/TextFieldAnimationExtension/b2d50c77c8a673c1dd6f94dd8e71c66c8c1515ef/TextFieldAnimationSample/TextFieldAnimationSample.xcodeproj/project.xcworkspace/xcuserdata/seedante.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /TextFieldAnimationSample/TextFieldAnimationSample.xcodeproj/xcuserdata/seedante.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /TextFieldAnimationSample/TextFieldAnimationSample.xcodeproj/xcuserdata/seedante.xcuserdatad/xcschemes/TextFieldAnimationSample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /TextFieldAnimationSample/TextFieldAnimationSample.xcodeproj/xcuserdata/seedante.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | TextFieldAnimationSample.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | FDFA5F0C1C0D9FD00098837D 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /TextFieldAnimationSample/TextFieldAnimationSample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // RockYouTextFieldSample 4 | // 5 | // Created by seedante on 15/12/1. 6 | // Copyright © 2015年 seedante. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /TextFieldAnimationSample/TextFieldAnimationSample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /TextFieldAnimationSample/TextFieldAnimationSample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /TextFieldAnimationSample/TextFieldAnimationSample/Assets.xcassets/love.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "hearts-32.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "hearts-64.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "hearts-128.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /TextFieldAnimationSample/TextFieldAnimationSample/Assets.xcassets/love.imageset/hearts-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seedante/TextFieldAnimationExtension/b2d50c77c8a673c1dd6f94dd8e71c66c8c1515ef/TextFieldAnimationSample/TextFieldAnimationSample/Assets.xcassets/love.imageset/hearts-128.png -------------------------------------------------------------------------------- /TextFieldAnimationSample/TextFieldAnimationSample/Assets.xcassets/love.imageset/hearts-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seedante/TextFieldAnimationExtension/b2d50c77c8a673c1dd6f94dd8e71c66c8c1515ef/TextFieldAnimationSample/TextFieldAnimationSample/Assets.xcassets/love.imageset/hearts-32.png -------------------------------------------------------------------------------- /TextFieldAnimationSample/TextFieldAnimationSample/Assets.xcassets/love.imageset/hearts-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seedante/TextFieldAnimationExtension/b2d50c77c8a673c1dd6f94dd8e71c66c8c1515ef/TextFieldAnimationSample/TextFieldAnimationSample/Assets.xcassets/love.imageset/hearts-64.png -------------------------------------------------------------------------------- /TextFieldAnimationSample/TextFieldAnimationSample/Assets.xcassets/node.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "node.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "node@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "node@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /TextFieldAnimationSample/TextFieldAnimationSample/Assets.xcassets/node.imageset/node.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seedante/TextFieldAnimationExtension/b2d50c77c8a673c1dd6f94dd8e71c66c8c1515ef/TextFieldAnimationSample/TextFieldAnimationSample/Assets.xcassets/node.imageset/node.png -------------------------------------------------------------------------------- /TextFieldAnimationSample/TextFieldAnimationSample/Assets.xcassets/node.imageset/node@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seedante/TextFieldAnimationExtension/b2d50c77c8a673c1dd6f94dd8e71c66c8c1515ef/TextFieldAnimationSample/TextFieldAnimationSample/Assets.xcassets/node.imageset/node@2x.png -------------------------------------------------------------------------------- /TextFieldAnimationSample/TextFieldAnimationSample/Assets.xcassets/node.imageset/node@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seedante/TextFieldAnimationExtension/b2d50c77c8a673c1dd6f94dd8e71c66c8c1515ef/TextFieldAnimationSample/TextFieldAnimationSample/Assets.xcassets/node.imageset/node@3x.png -------------------------------------------------------------------------------- /TextFieldAnimationSample/TextFieldAnimationSample/Assets.xcassets/smallStar.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "smallStar.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "smallStar@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /TextFieldAnimationSample/TextFieldAnimationSample/Assets.xcassets/smallStar.imageset/smallStar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seedante/TextFieldAnimationExtension/b2d50c77c8a673c1dd6f94dd8e71c66c8c1515ef/TextFieldAnimationSample/TextFieldAnimationSample/Assets.xcassets/smallStar.imageset/smallStar.png -------------------------------------------------------------------------------- /TextFieldAnimationSample/TextFieldAnimationSample/Assets.xcassets/smallStar.imageset/smallStar@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seedante/TextFieldAnimationExtension/b2d50c77c8a673c1dd6f94dd8e71c66c8c1515ef/TextFieldAnimationSample/TextFieldAnimationSample/Assets.xcassets/smallStar.imageset/smallStar@2x.png -------------------------------------------------------------------------------- /TextFieldAnimationSample/TextFieldAnimationSample/Assets.xcassets/two_hearts.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "two_hearts-25.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "two_hearts-32.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "two_hearts-50.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /TextFieldAnimationSample/TextFieldAnimationSample/Assets.xcassets/two_hearts.imageset/two_hearts-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seedante/TextFieldAnimationExtension/b2d50c77c8a673c1dd6f94dd8e71c66c8c1515ef/TextFieldAnimationSample/TextFieldAnimationSample/Assets.xcassets/two_hearts.imageset/two_hearts-25.png -------------------------------------------------------------------------------- /TextFieldAnimationSample/TextFieldAnimationSample/Assets.xcassets/two_hearts.imageset/two_hearts-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seedante/TextFieldAnimationExtension/b2d50c77c8a673c1dd6f94dd8e71c66c8c1515ef/TextFieldAnimationSample/TextFieldAnimationSample/Assets.xcassets/two_hearts.imageset/two_hearts-32.png -------------------------------------------------------------------------------- /TextFieldAnimationSample/TextFieldAnimationSample/Assets.xcassets/two_hearts.imageset/two_hearts-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seedante/TextFieldAnimationExtension/b2d50c77c8a673c1dd6f94dd8e71c66c8c1515ef/TextFieldAnimationSample/TextFieldAnimationSample/Assets.xcassets/two_hearts.imageset/two_hearts-50.png -------------------------------------------------------------------------------- /TextFieldAnimationSample/TextFieldAnimationSample/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /TextFieldAnimationSample/TextFieldAnimationSample/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 78 | 85 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /TextFieldAnimationSample/TextFieldAnimationSample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /TextFieldAnimationSample/TextFieldAnimationSample/SDETextFieldExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RockYouTextFieldExtension.swift 3 | // RockYouTextFieldSample 4 | // 5 | // Created by seedante on 15/12/2. 6 | // Copyright © 2015年 seedante. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | private var CurrentContextTextKey: UInt8 = 0 13 | private var ColorModeKey: UInt8 = 1 14 | private var InputAnimationEnabledKey: UInt8 = 2 15 | private var EmitterLayerKey: UInt8 = 3 16 | private var EmittingKey: UInt8 = 4 17 | private var InputAnimationTextKey: UInt8 = 5 18 | 19 | extension UITextField{ 20 | private var emitterLayer: CAEmitterLayer{ 21 | get { 22 | return objc_getAssociatedObject(self, &EmitterLayerKey) as! CAEmitterLayer 23 | } 24 | set(newValue) { 25 | objc_setAssociatedObject(self, &EmitterLayerKey, newValue, .OBJC_ASSOCIATION_RETAIN) 26 | } 27 | } 28 | 29 | private var currentContextText: String?{ 30 | get { 31 | return objc_getAssociatedObject(self, &CurrentContextTextKey) as? String 32 | } 33 | set(newValue) { 34 | objc_setAssociatedObject(self, &CurrentContextTextKey, newValue, .OBJC_ASSOCIATION_RETAIN) 35 | } 36 | } 37 | 38 | private var emitting: Bool{ 39 | get { 40 | return objc_getAssociatedObject(self, &EmittingKey) as! Bool 41 | } 42 | set(newValue) { 43 | objc_setAssociatedObject(self, &EmittingKey, newValue, .OBJC_ASSOCIATION_RETAIN) 44 | } 45 | } 46 | 47 | private var colors: [UIColor]{ 48 | return [UIColor.blackColor(), UIColor.darkGrayColor(), UIColor.lightGrayColor(), UIColor.whiteColor(), UIColor.grayColor(), UIColor.redColor(), UIColor.greenColor(), UIColor.blueColor(), 49 | UIColor.cyanColor(), UIColor.yellowColor(), UIColor.magentaColor(), UIColor.orangeColor(), UIColor.purpleColor(), UIColor.brownColor()] 50 | } 51 | 52 | //MARK: Custom Options 53 | //Text Color for deleted letter to throw: textfield's text color or random system color. 54 | var colorDeleteAnimationEnabled: Bool{ 55 | get { 56 | return objc_getAssociatedObject(self, &ColorModeKey) as! Bool 57 | } 58 | set(newValue) { 59 | objc_setAssociatedObject(self, &ColorModeKey, newValue, .OBJC_ASSOCIATION_RETAIN) 60 | } 61 | } 62 | 63 | //Input Effect Switch 64 | var inputAnimationEnabled: Bool{ 65 | get { 66 | return objc_getAssociatedObject(self, &InputAnimationEnabledKey) as! Bool 67 | } 68 | set(newValue) { 69 | objc_setAssociatedObject(self, &InputAnimationEnabledKey, newValue, .OBJC_ASSOCIATION_RETAIN) 70 | } 71 | } 72 | //Content of Input Animation 73 | var inputAnimationText: String?{ 74 | get { 75 | return objc_getAssociatedObject(self, &InputAnimationTextKey) as? String 76 | } 77 | set(newValue) { 78 | objc_setAssociatedObject(self, &InputAnimationTextKey, newValue, .OBJC_ASSOCIATION_RETAIN) 79 | } 80 | 81 | } 82 | 83 | //MARK: Config your textfield with one line code. 84 | func addSDEEffect(){ 85 | currentContextText = "" 86 | colorDeleteAnimationEnabled = false 87 | inputAnimationEnabled = false 88 | emitting = false 89 | setUpEmitterLayer() 90 | 91 | self.addTarget(self, action: "SDE_textDidChanged:", forControlEvents: .EditingChanged) 92 | } 93 | 94 | func removeSDEEffect(){ 95 | emitterLayer.removeFromSuperlayer() 96 | self.removeTarget(self, action: "SDE_textDidChanged:", forControlEvents: .EditingChanged) 97 | } 98 | 99 | //MARK: Input Animation 100 | func enableInputEffect(){ 101 | inputAnimationEnabled = true 102 | } 103 | 104 | func disableInputEffect(){ 105 | inputAnimationEnabled = false 106 | } 107 | 108 | private func setUpEmitterLayer() { 109 | clipsToBounds = false 110 | emitterLayer = CAEmitterLayer() 111 | emitterLayer.frame = bounds 112 | emitterLayer.seed = UInt32(NSDate().timeIntervalSince1970) 113 | layer.addSublayer(emitterLayer) 114 | } 115 | 116 | private func degreesToRadians(degrees: Double) -> CGFloat { 117 | return CGFloat(degrees * M_PI / 180.0) 118 | } 119 | 120 | private func imageOfText(drawText: NSString, textColor: UIColor)->UIImage{ 121 | UIGraphicsBeginImageContext(CGSize(width: 20, height: 20)) 122 | 123 | let textFont: UIFont = UIFont(name: "Helvetica Bold", size: 14)! 124 | let textFontAttributes = [ 125 | NSFontAttributeName: textFont, 126 | NSForegroundColorAttributeName: textColor, 127 | ] 128 | let rect: CGRect = CGRect(origin: CGPointZero, size: CGSize(width: 20, height: 20)) 129 | drawText.drawInRect(rect, withAttributes: textFontAttributes) 130 | let textImage: UIImage = UIGraphicsGetImageFromCurrentImageContext() 131 | 132 | UIGraphicsEndImageContext() 133 | return textImage 134 | 135 | } 136 | private func setUpEmitterCell() { 137 | let emitterCell = CAEmitterCell() 138 | 139 | if let emitterContentText = inputAnimationText{ 140 | emitterCell.contents = imageOfText(emitterContentText, textColor: textColor!).CGImage 141 | } 142 | 143 | emitterCell.enabled = true 144 | emitterCell.contentsRect = CGRect(origin: CGPointZero, size: CGSize(width: 1, height: 1)) 145 | emitterCell.color = UIColor.whiteColor().CGColor//UIColor(hue: 0.0, saturation: 0.0, brightness: 0.0, alpha: 1.0).CGColor 146 | emitterCell.redRange = 0.0 147 | emitterCell.greenRange = 0.0 148 | emitterCell.blueRange = 0.0 149 | emitterCell.alphaRange = 0.0 150 | emitterCell.redSpeed = 0.0 151 | emitterCell.greenSpeed = 0.0 152 | emitterCell.blueSpeed = 0.0 153 | emitterCell.alphaSpeed = -0.5 154 | emitterCell.scale = 1 155 | emitterCell.scaleRange = 0.1 156 | emitterCell.scaleSpeed = 0.1 157 | 158 | let zeroDegreesInRadians = degreesToRadians(0.0) 159 | emitterCell.spin = degreesToRadians(130.0) 160 | emitterCell.spinRange = zeroDegreesInRadians 161 | emitterCell.emissionLatitude = zeroDegreesInRadians 162 | emitterCell.emissionLongitude = zeroDegreesInRadians 163 | emitterCell.emissionRange = degreesToRadians(360.0) 164 | 165 | emitterCell.lifetime = 0.5 166 | emitterCell.lifetimeRange = 0.3 167 | emitterCell.birthRate = 50.0 168 | emitterCell.velocity = 50.0 169 | emitterCell.velocityRange = 500.0 170 | emitterCell.xAcceleration = -500.0 171 | emitterCell.yAcceleration = -500.0 172 | emitterLayer.emitterCells = [emitterCell] 173 | } 174 | 175 | //MARK: Shake Animation(Useless) 176 | func shakeX(){ 177 | let shakeXAnimation = CAKeyframeAnimation(keyPath: "position.x") 178 | shakeXAnimation.values = [0, 10, -10, 10, 0] 179 | shakeXAnimation.keyTimes = [0, (1 / 6.0), (3 / 6.0), (5 / 6.0), 1] 180 | shakeXAnimation.additive = true 181 | 182 | layer.addAnimation(shakeXAnimation, forKey: nil) 183 | } 184 | 185 | func shakeY(){ 186 | let shakeYAnimation = CAKeyframeAnimation(keyPath: "position.y") 187 | shakeYAnimation.values = [0, -10, 10, -10, 0] 188 | shakeYAnimation.keyTimes = [0, (1 / 6.0), (3 / 6.0), (5 / 6.0), 1] 189 | shakeYAnimation.additive = true 190 | 191 | layer.addAnimation(shakeYAnimation, forKey: nil) 192 | } 193 | 194 | //MARK: Helper 195 | @objc private func removeDiffView(diffView: UIView){ 196 | diffView.removeFromSuperview() 197 | } 198 | 199 | private func diffTextColor() -> UIColor{ 200 | var color: UIColor 201 | if colorDeleteAnimationEnabled{ 202 | let index = Int(UInt32(arc4random()) % UInt32(colors.count)) 203 | color = colors[index] 204 | }else{ 205 | color = textColor! 206 | } 207 | 208 | return color 209 | } 210 | 211 | @objc private func SDE_textDidChanged(textField: UITextField){ 212 | var cursorOrigin = caretRectForPosition(selectedTextRange!.start).origin 213 | 214 | let diffText = diffBetween(currentContextText!, newString: textField.text!) 215 | if currentContextText?.characters.count > text?.characters.count{ 216 | var startPoint = convertPoint(CGPoint(x: (cursorOrigin.x + frame.height), y: -frame.height / 4), toView: superview) 217 | if startPoint.x > frame.origin.x + frame.size.width{ 218 | startPoint.x = frame.origin.x + frame.size.width 219 | } 220 | 221 | let endPositionX = startPoint.x - 200 - CGFloat(UInt32(arc4random()) % UInt32(50)) 222 | let endPositionY = startPoint.y + 50 - CGFloat(UInt32(arc4random()) % UInt32(100)) 223 | let endPoint = CGPoint(x: endPositionX, y: endPositionY) 224 | let controlPoint = CGPoint(x: (startPoint.x + endPoint.x) / 2, y: startPoint.y - frame.height) 225 | 226 | let throwOrigin = CGPoint(x: (endPoint.x - frame.height / 2), y: (endPoint.y - frame.height / 2)) 227 | let viewSize = CGSize(width: frame.size.height, height: frame.size.height) 228 | let containerView = UIView(frame: CGRect(origin: throwOrigin, size: viewSize)) 229 | containerView.layer.opacity = 0 230 | 231 | //Maybe you want throw other things. 232 | let diffLabel = UILabel(frame: CGRect(origin: CGPointZero, size: viewSize)) 233 | diffLabel.font = font 234 | diffLabel.textColor = diffTextColor() 235 | diffLabel.adjustsFontSizeToFitWidth = true 236 | diffLabel.text = diffText 237 | 238 | containerView.addSubview(diffLabel) 239 | superview?.addSubview(containerView) 240 | 241 | let path = UIBezierPath() 242 | path.moveToPoint(startPoint) 243 | path.addQuadCurveToPoint(endPoint, controlPoint: controlPoint) 244 | let curvemoveAnimation = CAKeyframeAnimation(keyPath: "position") 245 | curvemoveAnimation.path = path.CGPath 246 | 247 | let rotateAnimation = CABasicAnimation(keyPath: "transform") 248 | let rotateRatio = CGFloat(UInt32(arc4random()) % UInt32(10)) / 10.0 249 | rotateAnimation.toValue = NSValue(CATransform3D: CATransform3DMakeRotation(CGFloat(M_PI_2), 0, 0, rotateRatio)) 250 | 251 | let fadeAnimation = CABasicAnimation(keyPath: "opacity") 252 | fadeAnimation.fromValue = 1.0 253 | fadeAnimation.toValue = 0.5 254 | 255 | let animationGroup = CAAnimationGroup() 256 | animationGroup.animations = [curvemoveAnimation, rotateAnimation, fadeAnimation] 257 | animationGroup.duration = 0.5 258 | 259 | containerView.layer.addAnimation(animationGroup, forKey: nil) 260 | performSelector("removeDiffView:", withObject: diffLabel, afterDelay: 0.51) 261 | }else if inputAnimationEnabled{ 262 | if !emitting{ 263 | setUpEmitterCell() 264 | emitting = true 265 | }else{ 266 | NSObject.cancelPreviousPerformRequestsWithTarget(self) 267 | } 268 | 269 | //Why 10 here, cursorOrigin returned by 'caretRectForPosition(selectedTextRange!.start).origin' is always not same with real position. 10 work fine with system font size from 14 to 30 most time. Calculation for accurate value is painful for me, if you know simple way, please tell me. 270 | if cursorOrigin.x > frame.size.width{ 271 | cursorOrigin.x = frame.size.width - 10 272 | }else{ 273 | cursorOrigin.x += 10 274 | } 275 | emitterLayer.emitterPosition = cursorOrigin 276 | performSelector("shutdownEmitterLayer", withObject: nil, afterDelay: 0.5) 277 | } 278 | 279 | currentContextText = text 280 | } 281 | 282 | @objc private func shutdownEmitterLayer(){ 283 | emitting = false 284 | emitterLayer.emitterCells = nil 285 | } 286 | 287 | private func diffBetween(oldString: String, newString: String) -> String{ 288 | if oldString.characters.count == 0{ 289 | return newString 290 | } 291 | 292 | if newString.characters.count == 0{ 293 | return oldString 294 | } 295 | 296 | var diffCharacter: Character = Character("X") 297 | let startIndex = oldString.startIndex 298 | let oldLastIndex = oldString.startIndex.advancedBy(oldString.characters.count - 1) 299 | let newLastIndex = newString.startIndex.advancedBy(newString.characters.count - 1) 300 | for (index, charactor) in oldString.characters.enumerate(){ 301 | let checkIndex = startIndex.advancedBy(index) 302 | let anotherCharacter = newString[checkIndex] 303 | 304 | if checkIndex == oldLastIndex{ 305 | if charactor == anotherCharacter{ 306 | //XY: XYZ 307 | diffCharacter = newString[newLastIndex] 308 | }else{ 309 | //XY: XZY 310 | diffCharacter = anotherCharacter 311 | } 312 | break 313 | } 314 | 315 | if checkIndex == newLastIndex{ 316 | if charactor == anotherCharacter{ 317 | //XYZ: XY 318 | diffCharacter = oldString[oldLastIndex] 319 | }else{ 320 | //XYZ: XZ 321 | diffCharacter = oldString[checkIndex] 322 | } 323 | break 324 | } 325 | 326 | if charactor != anotherCharacter{ 327 | if newString.characters.count > oldString.characters.count{ 328 | //OXYZ: OKXYZ 329 | diffCharacter = anotherCharacter 330 | }else{ 331 | //OXYZ: OYZ 332 | diffCharacter = charactor 333 | } 334 | break 335 | } 336 | } 337 | 338 | return String(diffCharacter) 339 | } 340 | 341 | } -------------------------------------------------------------------------------- /TextFieldAnimationSample/TextFieldAnimationSample/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // RockYouTextFieldSample 4 | // 5 | // Created by seedante on 15/12/1. 6 | // Copyright © 2015年 seedante. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController, UITextFieldDelegate { 12 | 13 | var letUsRock = false 14 | var willThrowColorText = false 15 | @IBOutlet weak var AnimationTextField: UITextField! 16 | @IBOutlet weak var inputEffectTextField: UITextField! 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | // Do any additional setup after loading the view, typically from a nib. 20 | AnimationTextField.delegate = self 21 | inputEffectTextField.delegate = self 22 | AnimationTextField.addSDEEffect() 23 | //❤️💕💞💗💖💛💚💙💜❣️💝💟💖💘😂⚽️ 24 | //AnimationTextField.inputAnimationText = "❤️" 25 | } 26 | 27 | 28 | @IBAction func switchOnorOFFInputSkakeEffect(sender: UISegmentedControl) { 29 | letUsRock = sender.selectedSegmentIndex == 0 ? false : true 30 | } 31 | 32 | @IBAction func switchOnorOffColorTextEffect(sender: UISegmentedControl) { 33 | AnimationTextField.colorDeleteAnimationEnabled = sender.selectedSegmentIndex == 0 ? false : true 34 | } 35 | 36 | @IBAction func switchOnorOffInputEffect(sender: UISegmentedControl) { 37 | AnimationTextField.inputAnimationEnabled = sender.selectedSegmentIndex == 0 ? false : true 38 | AnimationTextField.inputAnimationText = inputEffectTextField.text 39 | } 40 | 41 | 42 | func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool { 43 | if textField == AnimationTextField{ 44 | if string != "" && letUsRock{ 45 | AnimationTextField.shakeY() 46 | } 47 | }else{ 48 | AnimationTextField.inputAnimationText = inputEffectTextField.text 49 | } 50 | 51 | 52 | return true 53 | } 54 | } 55 | 56 | --------------------------------------------------------------------------------