Compare commits

...

60 Commits

Author SHA1 Message Date
Alexey Gerasimov 51067b408f
Merge pull request #13 from TouchInstinct/feature/swift4.2
Fixed for Swift4.2
2018-10-16 11:17:37 +03:00
Alexey Gerasimov b78e31424b Fixed for Swift4.2 2018-10-16 11:07:28 +03:00
Igor Kislyuk ee5ef07506
Merge pull request #12 from TouchInstinct/fix/swift-version
Add swift version
2017-12-04 11:53:00 +03:00
Igor Kislyuk 2c186fb3de Add swift version 2017-12-04 11:50:30 +03:00
Igor Kislyuk aee22c2cef
Merge pull request #11 from TouchInstinct/fix/setting-initial-date
Fix/setting initial date
2017-12-04 11:43:50 +03:00
Igor Kislyuk cf9b3a72c5 PR fix 2017-12-04 11:42:52 +03:00
Igor Kislyuk 9bb955a4fe Fix bug with animation 2017-11-20 17:48:55 +03:00
Igor Kislyuk 9dea74461e Update for swift 4 2017-11-18 19:54:14 +03:00
Igor Kislyuk 17ddd2c3b7 Fix access level 2017-11-12 01:30:41 +03:00
Igor Kislyuk 7041eb4107 Update
- fix swift 4 warnings
- add posibility to disable actions
- add functionality to disable moving cursor
2017-11-12 01:25:06 +03:00
Igor Kislyuk 7a0d673396 Fix done title 2017-10-30 10:27:46 +03:00
Igor Kislyuk c5bf32e147 Update to background color 2017-10-30 10:23:59 +03:00
Igor Kislyuk 4adfe1489c Update for setting date 2017-10-18 02:43:44 +03:00
Nikolai Ashanin 297ef68850 Merge pull request #9 from TouchInstinct/fix/focus-bug
Fix. Bug with no-value after textfield lose it focus
2017-05-18 23:04:19 +03:00
Igor Kislyuk cba697954b Fix. Update pod spec 2017-05-18 22:35:17 +03:00
Igor Kislyuk de0e9adcab Fix. Bug with no-value after textfield lose it focus 2017-05-18 22:34:29 +03:00
Nikolai Ashanin 352af583cc Merge pull request #8 from TouchInstinct/bugfix/kvo
Fix. KVO & value passing
2017-04-27 13:51:35 +03:00
Igor Kislyuk 1425bbe004 Fix. KVO & value passing 2017-04-27 13:43:37 +03:00
Grigory 11c69147ed Merge pull request #7 from TouchInstinct/leftAlignmentFix
Left alignment fix
2017-04-11 16:36:33 +03:00
Grigory 1d9541158e new version 2017-04-11 16:34:38 +03:00
Grigory c2a78d6b7b animation fixed 2017-04-11 16:32:52 +03:00
Grigory 720d0f404c Merge pull request #6 from TouchInstinct/bugfix/colorChangeUIUpdate
new pod version
2017-01-26 16:02:51 +03:00
Grigory Ulanov c635cd1688 new pod version 2017-01-26 16:02:17 +03:00
Grigory 40ad393cbe Merge pull request #5 from TouchInstinct/bugfix/colorChangeUIUpdate
colors fixed
2017-01-26 16:01:24 +03:00
Grigory Ulanov 7883afcd74 colors fixed 2017-01-26 15:59:30 +03:00
Grigory 6924be8cee Merge pull request #4 from TouchInstinct/feature/open
open functions
2017-01-17 17:50:57 +03:00
Grigory Ulanov ad91c59c5b open functions 2017-01-17 17:32:34 +03:00
Grigory 8278bf17e3 Merge pull request #3 from TouchInstinct/bugfix/dateFormatter
dateformatter fixed
2017-01-12 17:53:26 +03:00
Grigory Ulanov e0c5ca347e dateformatter fixed 2017-01-12 17:52:24 +03:00
Nikolai Ashanin f5d6144142 Merge pull request #2 from TouchInstinct/fix/editPodspec
Edit pod spec
2016-12-23 14:38:28 +03:00
Ivan Zinovyev 81141f5784 Edit pod spec 2016-12-23 14:37:41 +03:00
Ivan Zinovyev 651a930ac3 Edit read 2016-12-20 19:35:55 +03:00
Ivan Zinovyev 808c549913 Edit read 2016-12-20 19:31:36 +03:00
Ivan Zinovyev 1717e00407 Change version 2016-12-20 18:34:20 +03:00
Ivan Zinovyev 0117bb73e1 Edit readme 2016-12-20 18:24:24 +03:00
Ivan Zinovyev 5dcea5e0e1 Make doneTitle instance var instead of static let 2016-12-20 18:10:44 +03:00
Ivan Zinovyev 37edd38260 Edit read 2016-12-20 18:07:45 +03:00
Ivan Zinovyev afc41c735f Edit read 2016-12-20 18:03:49 +03:00
Ivan Zinovyev 0c72961320 Edit read 2016-12-20 17:51:10 +03:00
Ivan Zinovyev 61efe9044b Edit readme 2016-12-20 17:45:00 +03:00
Ivan Zinovyev 13e015ba5d Edit readme 2016-12-20 17:44:00 +03:00
Ivan Zinovyev d983a40162 Edit read 2016-12-20 17:37:03 +03:00
Ivan Zinovyev 6549e0689e Edit read 2016-12-20 17:33:45 +03:00
Ivan Zinovyev 0c9dbea890 Edit read 2016-12-20 17:15:51 +03:00
Ivan Zinovyev 0aeff35e5c Merge pull request #1 from iznv/fixes
Fixes
2016-12-20 15:45:12 +03:00
Ivan Zinovyev 467797928e Change version 2016-12-20 15:44:31 +03:00
Ivan Zinovyev 3225462ea8 Add some MARKs 2016-12-20 15:10:30 +03:00
Ivan Zinovyev 0b8e6426bf Change read 2016-12-19 22:42:30 +03:00
Ivan Zinovyev 4008405d41 Change read 2016-12-19 22:40:02 +03:00
Ivan Zinovyev 6ede9f5e21 Edit podspec 2016-12-19 22:31:54 +03:00
Ivan Zinovyev 341f6c600f Delete _Pods.xcodeproj 2016-12-19 22:18:30 +03:00
Ivan Zinovyev f3d0fe080f Move space to constants 2016-12-19 22:12:50 +03:00
Ivan Zinovyev 914e6524ec Replace DateFormatter with Date extension, add dateFormat property for TextField 2016-12-19 22:12:22 +03:00
Ivan Zinovyev 4f24e4f9cc Add license to all source files 2016-12-19 19:33:48 +03:00
Ivan Zinovyev 857d5b7bec Add workspace and edit gitignore (ignore pods folder) 2016-12-19 18:32:45 +03:00
Ivan Zinovyev 9dce982693 Change pod version 2016-12-19 18:27:05 +03:00
Ivan Zinovyev 779290d1d6 Add done title as property 2016-12-19 18:23:59 +03:00
Ivan Zinovyev 222db00731 Remove redundant properties 2016-12-19 18:23:17 +03:00
Ivan Zinovyev 4b10040099 Make all internal things public 2016-12-19 17:38:26 +03:00
Ivan Zinovyev 51dc2c6137 Make some things public 2016-12-19 17:28:03 +03:00
24 changed files with 538 additions and 280 deletions

3
.gitignore vendored
View File

@ -30,4 +30,5 @@ Carthage
# Note: if you ignore the Pods directory, make sure to uncomment # Note: if you ignore the Pods directory, make sure to uncomment
# `pod install` in .travis.yml # `pod install` in .travis.yml
# #
# Pods/ Pods/
Podfile.lock

1
.swift-version Normal file
View File

@ -0,0 +1 @@
4.0

5
CHANGELOG.md Normal file
View File

@ -0,0 +1,5 @@
# Changelog
## 0.2.0
- **Fix**: date setting after picker presented.

View File

@ -12,7 +12,7 @@
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
12D59B30305289F1A0BAB148 /* UIAnimatedTextField.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = UIAnimatedTextField.podspec; path = ../UIAnimatedTextField.podspec; sourceTree = "<group>"; }; 12D59B30305289F1A0BAB148 /* UIAnimatedTextField.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = UIAnimatedTextField.podspec; path = ../UIAnimatedTextField.podspec; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
3023902FA98A29EAE27CE743 /* Pods_UIAnimatedTextField_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_UIAnimatedTextField_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3023902FA98A29EAE27CE743 /* Pods_UIAnimatedTextField_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_UIAnimatedTextField_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
305D07AC233331112B594740 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = "<group>"; }; 305D07AC233331112B594740 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = "<group>"; };
3EE19024E4B841DD8F1FEA61 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; }; 3EE19024E4B841DD8F1FEA61 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
@ -110,7 +110,6 @@
607FACE21AFB9204008FA782 /* Frameworks */, 607FACE21AFB9204008FA782 /* Frameworks */,
607FACE31AFB9204008FA782 /* Resources */, 607FACE31AFB9204008FA782 /* Resources */,
4EB68626CEDBCD2943AF13ED /* [CP] Embed Pods Frameworks */, 4EB68626CEDBCD2943AF13ED /* [CP] Embed Pods Frameworks */,
AFE96432D6D5B8E3FF680C55 /* [CP] Copy Pods Resources */,
); );
buildRules = ( buildRules = (
); );
@ -128,12 +127,12 @@
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastSwiftUpdateCheck = 0720; LastSwiftUpdateCheck = 0720;
LastUpgradeCheck = 0820; LastUpgradeCheck = 1000;
ORGANIZATIONNAME = CocoaPods; ORGANIZATIONNAME = CocoaPods;
TargetAttributes = { TargetAttributes = {
607FACE41AFB9204008FA782 = { 607FACE41AFB9204008FA782 = {
CreatedOnToolsVersion = 6.3.1; CreatedOnToolsVersion = 6.3.1;
LastSwiftMigration = 0820; LastSwiftMigration = 0910;
TestTargetID = 607FACCF1AFB9204008FA782; TestTargetID = 607FACCF1AFB9204008FA782;
}; };
}; };
@ -173,43 +172,34 @@
files = ( files = (
); );
inputPaths = ( inputPaths = (
"${SRCROOT}/Pods/Target Support Files/Pods-UIAnimatedTextField_Tests/Pods-UIAnimatedTextField_Tests-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/UIAnimatedTextField/UIAnimatedTextField.framework",
); );
name = "[CP] Embed Pods Frameworks"; name = "[CP] Embed Pods Frameworks";
outputPaths = ( outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/UIAnimatedTextField.framework",
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-UIAnimatedTextField_Tests/Pods-UIAnimatedTextField_Tests-frameworks.sh\"\n"; shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-UIAnimatedTextField_Tests/Pods-UIAnimatedTextField_Tests-frameworks.sh\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
AFE96432D6D5B8E3FF680C55 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-UIAnimatedTextField_Tests/Pods-UIAnimatedTextField_Tests-resources.sh\"\n";
showEnvVarsInLog = 0;
};
D458C72BE72F89E2185E2C11 /* [CP] Check Pods Manifest.lock */ = { D458C72BE72F89E2185E2C11 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
); );
inputPaths = ( inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
); );
name = "[CP] Check Pods Manifest.lock"; name = "[CP] Check Pods Manifest.lock";
outputPaths = ( outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-UIAnimatedTextField_Tests-checkManifestLockResult.txt",
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */
@ -234,14 +224,22 @@
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
@ -281,14 +279,22 @@
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
@ -326,7 +332,8 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0; SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.2;
}; };
name = Debug; name = Debug;
}; };
@ -339,7 +346,8 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0; SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.2;
}; };
name = Release; name = Release;
}; };

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "0820" LastUpgradeVersion = "1000"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:UIAnimatedTextField.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -1,4 +1,4 @@
Copyright (c) 2016 Ivan Zinovyev <ivan.zinovyev@touchin.ru> Copyright (c) 2016 Touch Instinct
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -1,24 +1,99 @@
# UIAnimatedTextField # UIAnimatedTextField
## Example This custom control can be used as a replacement for UITextField.
When an user taps on it, a placeholder rises smoothly.
To run the example project, clone the repo, and run `pod install` from the Example directory first. It comes with 5 different text types: simple, password, url, tappable, date.
## Requirements
## Installation ## Installation
UIAnimatedTextField is available through [CocoaPods](http://cocoapods.org). To install UIAnimatedTextField is available through [CocoaPods](http://cocoapods.org). To install
it, simply add the following line to your Podfile: it, simply add the pod 'UIAnimatedTextField' and the source for podspecs to your Podfile. For example:
```ruby ```ruby
pod "UIAnimatedTextField" source "https://github.com/iznv/Podspecs.git"
platform :ios, '9.0'
use_frameworks!
target "ProjectName" do
pod 'UIAnimatedTextField', '0.1.7'
end
``` ```
## Author ## Usage
Set height of UIView to 50 (optionally, to make UIAnimatedTextField look pretty). Create IBOutlet:
```swift
@IBOutlet weak var textField: UIAnimatedTextField!
```
Ivan Zinovyev, ivan.zinovyev@touchin.ru In order to enable placeholder, set placeholder property:
```swift
textField.placeholder = "Enter something"
```
### Simple type
By default you use simple type. It is just a text field.
<img src="https://raw.githubusercontent.com/iznv/UIAnimatedTextField/master/UIAnimatedTextField/Screenshots/simple1.png" width="300">
<img src="https://raw.githubusercontent.com/iznv/UIAnimatedTextField/master/UIAnimatedTextField/Screenshots/simple2.png" width="300">
### Password type
In order to use UIAnimatedTextField for password input, specify its type as .password
```swift
textField.type = .password
```
<img src="https://raw.githubusercontent.com/iznv/UIAnimatedTextField/master/UIAnimatedTextField/Screenshots/password.png" width="300">
### Date type
In order to use UIAnimatedTextField for date input, specify its type as .date
```swift
textField.type = .date
```
Also you can set date format and done button title:
```swift
// "Done" by default
textField.doneTitle = "Ok"
// "dd/MM/YYYY" by default
textField.dateFormat = "dd MMMM YYYY"
```
<img src="https://raw.githubusercontent.com/iznv/UIAnimatedTextField/master/UIAnimatedTextField/Screenshots/date.png" width="300">
### Tappable type
In order to choose somewhere something that will be displayed in text field, specify type as .tappable and designate an action, for example:
```swift
textField.type = .tappable(action: {textField in textField.text = "Selected thing" })
```
Tap on the field, do an action, display a result in text field.
## Customization
### Color
You can change color of placeholder, entered text, line like this:
```swift
textField.placeholderTopColor = .blue
textField.placeholderBottomColor = .brown
textField.enteredTextColor = .orange
textField.lineColor = .green
```
Result:
<img src="https://raw.githubusercontent.com/iznv/UIAnimatedTextField/master/UIAnimatedTextField/Screenshots/custom1.png" width="300">
<img src="https://raw.githubusercontent.com/iznv/UIAnimatedTextField/master/UIAnimatedTextField/Screenshots/custom2.png" width="300">
### Text Alignment
In order to change text alignment of placeholder and text field use this property:
```swift
textField.isLeftTextAlignment = true
```
Result:
<img src="https://raw.githubusercontent.com/iznv/UIAnimatedTextField/master/UIAnimatedTextField/Screenshots/custom3.png" width="300">
## License ## License
Copyright (c) 2016 Touch Instinct
UIAnimatedTextField is available under the MIT license. See the LICENSE file for more info. UIAnimatedTextField is available under the MIT license. See the LICENSE file for more info.

View File

@ -1,14 +1,14 @@
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = 'UIAnimatedTextField' s.name = 'UIAnimatedTextField'
s.version = '0.1.2' s.version = '0.3.0'
s.summary = 'UIAnimatedTextField' s.summary = 'UITextField with animated placeholder'
s.description = <<-DESC s.description = <<-DESC
Animated text field This custom control can be used as a replacement for UITextField. It comes with 5 different text types: simple, password, url, tappable, date.
DESC DESC
s.homepage = 'https://github.com/iznv/UIAnimatedTextField' s.homepage = 'https://github.com/TouchInstinct/UIAnimatedTextField'
s.license = { :type => 'MIT', :file => 'LICENSE' } s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { 'Ivan Zinovyev' => 'ivan.zinovyev@touchin.ru' } s.author = "Touch Instinct"
s.source = { :git => 'https://github.com/iznv/UIAnimatedTextField.git', :tag => s.version.to_s } s.source = { :git => 'https://github.com/TouchInstinct/UIAnimatedTextField.git', :tag => s.version.to_s }
s.ios.deployment_target = '8.0' s.ios.deployment_target = '8.0'
s.source_files = 'UIAnimatedTextField/Source/**/*' s.source_files = 'UIAnimatedTextField/Source/**/*'
s.frameworks = 'UIKit' s.frameworks = 'UIKit'

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View File

@ -0,0 +1,33 @@
//
// TIDateFormatter.swift
//
// Copyright (c) 2016 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
extension Date {
func toString(withFormat format: String) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = format
return dateFormatter.string(from: self)
}
}

View File

@ -1,17 +1,56 @@
// //
// EditableTextField.swift // EditableTextField.swift
// Pods
// //
// Created by Ivan Zinovyev on 12/19/16. // Copyright (c) 2016 Touch Instinct
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// //
import Foundation import Foundation
public class EditableTextField: UITextField { public enum EditableActionType {
case selectAll
var getType: (() -> TextType?)? case select
case cut
case copy
case paste
public static let allActions: [EditableActionType] = [.selectAll, .select, .cut, .paste, .copy]
}
open class EditableTextField: UITextField {
/// Actions, that will be disabled for this textField.
/// By default no actions are disabled.
open var disabledActions: [EditableActionType] = []
/// Allows to disable moving cursor for user
open var pinCursorToEnd: Bool = false
open var getType: (() -> TextType?)?
// MARK: - Private
private var disabledSelectors: [Selector] {
return disabledActions.map { selector(from: $0) }
}
private let menuSelectors = [ private let menuSelectors = [
#selector(selectAll(_:)), #selector(selectAll(_:)),
#selector(select(_:)), #selector(select(_:)),
@ -19,8 +58,14 @@ public class EditableTextField: UITextField {
#selector(copy(_:)), #selector(copy(_:)),
#selector(paste(_:)) #selector(paste(_:))
] ]
// MARK: - Overriden
override public func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { override open func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if disabledSelectors.contains(action) {
return false
}
guard let type = getType?() else { guard let type = getType?() else {
return super.canPerformAction(action, withSender: sender) return super.canPerformAction(action, withSender: sender)
} }
@ -34,7 +79,7 @@ public class EditableTextField: UITextField {
return super.canPerformAction(action, withSender: sender) return super.canPerformAction(action, withSender: sender)
} }
override public func caretRect(for position: UITextPosition) -> CGRect { override open func caretRect(for position: UITextPosition) -> CGRect {
guard let type = getType?() else { guard let type = getType?() else {
return super.caretRect(for: position) return super.caretRect(for: position)
} }
@ -45,5 +90,33 @@ public class EditableTextField: UITextField {
return super.caretRect(for: position) return super.caretRect(for: position)
} }
override open func closestPosition(to point: CGPoint) -> UITextPosition? {
if pinCursorToEnd {
return endOfDocument
}
return super.closestPosition(to: point)
}
} }
// MARK: - Private extensions
private extension UITextField {
func selector(from actionTyoe: EditableActionType) -> Selector {
switch actionTyoe {
case .selectAll:
return #selector(selectAll(_:))
case .select:
return #selector(select(_:))
case .cut:
return #selector(cut(_:))
case .copy:
return #selector(copy(_:))
case .paste:
return #selector(paste(_:))
}
}
}

View File

@ -1,23 +1,31 @@
// //
// String+Extensions.swift // String+Extensions.swift
// Pods //
// Copyright (c) 2016 Touch Instinct
// //
// Created by Ivan Zinovyev on 12/19/16. // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// //
import Foundation import Foundation
extension String { extension String {
var localized: String {
return NSLocalizedString(self, comment: "")
}
var isMultiline: Bool {
return range(of: "\n") != nil
}
var isValidEmail: Bool { var isValidEmail: Bool {
let emailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}" let emailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}"
let emailPredicat = NSPredicate(format: "SELF MATCHES %@", emailRegex) let emailPredicat = NSPredicate(format: "SELF MATCHES %@", emailRegex)

View File

@ -1,46 +0,0 @@
//
// TIDateFormatter.swift
// Pods
//
// Created by Ivan Zinovyev on 12/19/16.
//
//
import Foundation
class TIDateFormatter {
fileprivate static let dateLongFormat = "dd/MM/YYYY"
fileprivate static let dateShortFormat = "dd/MM/YY"
fileprivate static let monthDayFormat = "MMMM d"
fileprivate let longDateFormatter = DateFormatter()
fileprivate let shortDateFormatter = DateFormatter()
fileprivate let monthDayDateFormatter = DateFormatter()
private static let shared = TIDateFormatter()
// MARK: Init
private init() {
longDateFormatter.dateFormat = TIDateFormatter.dateLongFormat
shortDateFormatter.dateFormat = TIDateFormatter.dateShortFormat
monthDayDateFormatter.dateFormat = TIDateFormatter.monthDayFormat
}
// MARK: Public functions
static func longDate(from date: Date) -> String {
return shared.longDateFormatter.string(from: date)
}
static func shortDate(from date: Date) -> String {
return shared.shortDateFormatter.string(from: date)
}
/// - Returns: example: "December 2"
static func monthDay(from date: Date) -> String {
return shared.monthDayDateFormatter.string(from: date)
}
}

View File

@ -1,15 +1,31 @@
// //
// UIAnimatedTextField.swift // UIAnimatedTextField.swift
// Pods
// //
// Created by Ivan Zinovyev on 12/19/16. // Copyright (c) 2016 Touch Instinct
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// //
import UIKit import UIKit
@objc protocol UIAnimatedTextFieldDelegate: class { @objc public protocol UIAnimatedTextFieldDelegate: class {
@objc optional func animatedTextFieldValueDidChange(_ animatedTextField: UIAnimatedTextField) @objc optional func animatedTextFieldValueDidChange(_ animatedTextField: UIAnimatedTextField)
@objc optional func animatedTextFieldWillReactForTap() @objc optional func animatedTextFieldWillReactForTap()
@objc optional func animatedTextFieldShouldBeginEditing(_ animatedTextField: UIAnimatedTextField) -> Bool @objc optional func animatedTextFieldShouldBeginEditing(_ animatedTextField: UIAnimatedTextField) -> Bool
@ -21,15 +37,15 @@ import UIKit
replacementString string: String) -> Bool replacementString string: String) -> Bool
@objc optional func animatedTextFieldShouldClear(_ animatedTextField: UIAnimatedTextField) -> Bool @objc optional func animatedTextFieldShouldClear(_ animatedTextField: UIAnimatedTextField) -> Bool
@objc optional func animatedTextFieldShouldReturn(_ animatedTextField: UIAnimatedTextField) -> Bool @objc optional func animatedTextFieldShouldReturn(_ animatedTextField: UIAnimatedTextField) -> Bool
} }
enum AnimatedTextFieldState { public enum AnimatedTextFieldState {
case placeholder case placeholder
case text case text
} }
enum TextType { public enum TextType {
case simple case simple
case password case password
case url case url
@ -38,34 +54,30 @@ enum TextType {
} }
@IBDesignable @IBDesignable
public class UIAnimatedTextField: UIView { open class UIAnimatedTextField: UIView {
// MARK: - Constants
struct Constants {
static let done = "Done"
static let space = " "
static let defaultDateFormat = "dd/MM/yyyy"
}
// MARK: - Delegate // MARK: - Delegate
weak var delegate: UIAnimatedTextFieldDelegate? weak public var delegate: UIAnimatedTextFieldDelegate?
// MARK: - UI Properties // MARK: - UI Properties
private(set) public var textField: EditableTextField! private(set) public var textField: EditableTextField!
private(set) public var placeholderLabel: UILabel! private(set) public var placeholderLabel: UILabel!
private(set) public var lineView: UIView! private(set) public var lineView: UIView!
private var disclosureIndicatorImageView: UIImageView! private var disclosureIndicatorImageView: UIImageView!
// MARK: - Properties // MARK: - @IBInspectable Properties
var text: String? { @IBInspectable public var placeholder: String? {
get {
return textField.text
}
set {
textField.text = newValue
delegate?.animatedTextFieldValueDidChange?(self)
layoutSubviews()
}
}
@IBInspectable var placeholder: String? {
get { get {
return placeholderLabel.text return placeholderLabel.text
} }
@ -73,24 +85,8 @@ public class UIAnimatedTextField: UIView {
placeholderLabel.text = newValue placeholderLabel.text = newValue
} }
} }
var font: UIFont? { @IBInspectable public var isLeftTextAlignment: Bool {
get {
return placeholderLabel.font
}
set {
textField.font = newValue
placeholderLabel.font = newValue
}
}
var isDisclosureIndicatorVisible: Bool = false {
didSet {
disclosureIndicatorImageView.isHidden = !isDisclosureIndicatorVisible
}
}
@IBInspectable var isLeftTextAlignment: Bool {
get { get {
return textField.textAlignment == .left return textField.textAlignment == .left
} }
@ -100,8 +96,58 @@ public class UIAnimatedTextField: UIView {
placeholderLabel.textAlignment = alignment placeholderLabel.textAlignment = alignment
} }
} }
var type: TextType = .simple { @IBInspectable public var placeholderTopColor: UIColor = UIColor.gray {
didSet {
setState(toState: state)
}
}
@IBInspectable public var placeholderBottomColor: UIColor = UIColor.gray {
didSet {
setState(toState: state)
}
}
@IBInspectable public var enteredTextColor: UIColor {
get { return textField.textColor ?? UIColor.black }
set { textField.textColor = newValue }
}
@IBInspectable public var lineColor: UIColor {
get { return lineView.backgroundColor ?? UIColor.gray }
set { lineView.backgroundColor = newValue }
}
// MARK: - Public Properties
public var text: String? {
get {
return textField.text
}
set {
textField.text = newValue
delegate?.animatedTextFieldValueDidChange?(self)
layoutSubviews()
}
}
public var font: UIFont? {
get {
return placeholderLabel.font
}
set {
textField.font = newValue
placeholderLabel.font = newValue
}
}
public var isDisclosureIndicatorVisible: Bool = false {
didSet {
disclosureIndicatorImageView.isHidden = !isDisclosureIndicatorVisible
}
}
public var type: TextType = .simple {
didSet { didSet {
textField.isSecureTextEntry = false textField.isSecureTextEntry = false
textField.keyboardType = .default textField.keyboardType = .default
@ -110,15 +156,15 @@ public class UIAnimatedTextField: UIView {
textField.autocapitalizationType = .words textField.autocapitalizationType = .words
textField.inputView = nil textField.inputView = nil
textField.inputAccessoryView = nil textField.inputAccessoryView = nil
tapAction = nil tapAction = nil
isDisclosureIndicatorVisible = false isDisclosureIndicatorVisible = false
if let tapGestureRecognizer = tapGestureRecognizer { if let tapGestureRecognizer = tapGestureRecognizer {
removeGestureRecognizer(tapGestureRecognizer) removeGestureRecognizer(tapGestureRecognizer)
self.tapGestureRecognizer = nil self.tapGestureRecognizer = nil
} }
switch type { switch type {
case .password: case .password:
textField.isSecureTextEntry = true textField.isSecureTextEntry = true
@ -132,7 +178,7 @@ public class UIAnimatedTextField: UIView {
tapAction = action tapAction = action
textField.isUserInteractionEnabled = false textField.isUserInteractionEnabled = false
isDisclosureIndicatorVisible = true isDisclosureIndicatorVisible = true
tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapGestureRecognizerAction(_:))) tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapGestureRecognizerAction(_:)))
if let tapGestureRecognizer = tapGestureRecognizer { if let tapGestureRecognizer = tapGestureRecognizer {
addGestureRecognizer(tapGestureRecognizer) addGestureRecognizer(tapGestureRecognizer)
@ -147,53 +193,55 @@ public class UIAnimatedTextField: UIView {
} }
} }
} }
var selectedDate: Date? @objc public dynamic var selectedDate: Date?
public var dateFormat: String = Constants.defaultDateFormat
@IBInspectable var placeholderTopColor: UIColor = UIColor.gray
@IBInspectable var placeholderBottomColor: UIColor = UIColor.gray public var doneTitle: String = Constants.done {
didSet {
@IBInspectable var enteredTextColor: UIColor { textField.inputAccessoryView = getDateInputAccessoryView()
get { return textField.textColor ?? UIColor.black } }
set { textField.textColor = newValue }
} }
@IBInspectable var lineColor: UIColor { public var doneTitleColor: UIColor = .black {
get { return lineView.backgroundColor ?? UIColor.gray } didSet {
set { lineView.backgroundColor = newValue } textField.inputAccessoryView = getDateInputAccessoryView()
}
} }
// MARK: - Static Properties
static public let animationDuration: TimeInterval = 0.3
static public let disclosureIndicatorWidth = 15.0
// MARK: - Private Properties // MARK: - Private Properties
static let animationDuration: TimeInterval = 0.3
static let disclosureIndicatorWidth = 15.0
private var tapGestureRecognizer: UITapGestureRecognizer? private var tapGestureRecognizer: UITapGestureRecognizer?
private var tapAction: ((_ animatedTextField: UIAnimatedTextField) -> Void)? private var tapAction: ((_ animatedTextField: UIAnimatedTextField) -> Void)?
private var isShownInfo: Bool = false private var isShownInfo: Bool = false
private var state: AnimatedTextFieldState { private var state: AnimatedTextFieldState {
var state: AnimatedTextFieldState = .placeholder var state: AnimatedTextFieldState = .placeholder
if textField.text?.characters.count ?? 0 > 0 || textField.isFirstResponder { if textField.text?.count ?? 0 > 0 || textField.isFirstResponder {
state = .text state = .text
} }
return state return state
} }
// MARK: - Initialization // MARK: - Initialization
override init(frame: CGRect) { override public init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
initialization() initialization()
} }
required public init?(coder aDecoder: NSCoder) { required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder) super.init(coder: aDecoder)
initialization() initialization()
} }
private func initialization() { private func initialization() {
textField = EditableTextField() textField = EditableTextField()
textField.delegate = self textField.delegate = self
@ -203,7 +251,7 @@ public class UIAnimatedTextField: UIView {
return self?.type return self?.type
} }
addSubview(textField) addSubview(textField)
placeholderLabel = UILabel() placeholderLabel = UILabel()
placeholderLabel.isUserInteractionEnabled = false placeholderLabel.isUserInteractionEnabled = false
placeholderLabel.textColor = placeholderBottomColor placeholderLabel.textColor = placeholderBottomColor
@ -211,50 +259,50 @@ public class UIAnimatedTextField: UIView {
placeholderLabel.adjustsFontSizeToFitWidth = true placeholderLabel.adjustsFontSizeToFitWidth = true
placeholderLabel.minimumScaleFactor = 0.5 placeholderLabel.minimumScaleFactor = 0.5
addSubview(placeholderLabel) addSubview(placeholderLabel)
lineView = UIView() lineView = UIView()
lineView.backgroundColor = placeholderLabel.textColor lineView.backgroundColor = placeholderLabel.textColor
addSubview(lineView) addSubview(lineView)
disclosureIndicatorImageView = UIImageView() disclosureIndicatorImageView = UIImageView()
disclosureIndicatorImageView.image = UIImage(named: "disclosureIndicator") disclosureIndicatorImageView.image = UIImage(named: "disclosureIndicator")
disclosureIndicatorImageView.contentMode = .center disclosureIndicatorImageView.contentMode = .center
disclosureIndicatorImageView.isHidden = true disclosureIndicatorImageView.isHidden = true
addSubview(disclosureIndicatorImageView) addSubview(disclosureIndicatorImageView)
layoutSubviews() layoutSubviews()
} }
// MARK: - Layout // MARK: - Layout
private func textFieldFrame() -> CGRect { private func textFieldFrame() -> CGRect {
var size = bounds.size var size = bounds.size
var origin = bounds.origin var origin = bounds.origin
size.height = 2/3 * size.height size.height = 2/3 * size.height
origin.y = 1/3 * bounds.size.height origin.y = 1/3 * bounds.size.height
if isDisclosureIndicatorVisible { if isDisclosureIndicatorVisible {
origin.x += CGFloat(UIAnimatedTextField.disclosureIndicatorWidth) origin.x += CGFloat(UIAnimatedTextField.disclosureIndicatorWidth)
size.width -= CGFloat(UIAnimatedTextField.disclosureIndicatorWidth) * 2 size.width -= CGFloat(UIAnimatedTextField.disclosureIndicatorWidth) * 2
} }
let textFieldBounds = CGRect(origin: origin, size: size) let textFieldBounds = CGRect(origin: origin, size: size)
return textFieldBounds return textFieldBounds
} }
private func placeholderLabelFrame(state: AnimatedTextFieldState) -> CGRect { private func placeholderLabelFrame(state: AnimatedTextFieldState) -> CGRect {
if state == .placeholder { if state == .placeholder {
return textFieldFrame() return textFieldFrame()
} else { } else {
var size = bounds.size var size = bounds.size
size.height = 1/3 * size.height size.height = 1/3 * size.height
let placeholderLabelBounds = CGRect(origin: bounds.origin, size: size) let placeholderLabelBounds = CGRect(origin: bounds.origin, size: size)
return placeholderLabelBounds return placeholderLabelBounds
} }
} }
private func disclosureIndicatorFrame() -> CGRect { private func disclosureIndicatorFrame() -> CGRect {
let fieldFrame = textFieldFrame() let fieldFrame = textFieldFrame()
let frame = CGRect(x: bounds.width - CGFloat(UIAnimatedTextField.disclosureIndicatorWidth), let frame = CGRect(x: bounds.width - CGFloat(UIAnimatedTextField.disclosureIndicatorWidth),
@ -263,7 +311,7 @@ public class UIAnimatedTextField: UIView {
height: fieldFrame.height) height: fieldFrame.height)
return frame return frame
} }
private func placeholderLabelTransform(state: AnimatedTextFieldState) -> CGAffineTransform { private func placeholderLabelTransform(state: AnimatedTextFieldState) -> CGAffineTransform {
if state == .placeholder { if state == .placeholder {
return CGAffineTransform.identity return CGAffineTransform.identity
@ -271,27 +319,27 @@ public class UIAnimatedTextField: UIView {
return CGAffineTransform(scaleX: 0.8, y: 0.8) return CGAffineTransform(scaleX: 0.8, y: 0.8)
} }
} }
override public func layoutSubviews() { override open func layoutSubviews() {
super.layoutSubviews() super.layoutSubviews()
textField.frame = textFieldFrame() textField.frame = textFieldFrame()
if isDisclosureIndicatorVisible { if isDisclosureIndicatorVisible {
disclosureIndicatorImageView.frame = disclosureIndicatorFrame() disclosureIndicatorImageView.frame = disclosureIndicatorFrame()
} }
setState(toState: state, duration: 0) setState(toState: state, duration: 0)
lineView.frame = CGRect(x: 0, lineView.frame = CGRect(x: 0,
y: bounds.height - UIView.onePixelInPoints * 2, y: bounds.height - UIView.onePixelInPoints * 2,
width: bounds.width, width: bounds.width,
height: UIView.onePixelInPoints) height: UIView.onePixelInPoints)
} }
// MARK: - Animation // MARK: - Animation
func setState(toState state: AnimatedTextFieldState, duration: TimeInterval) { public func setState(toState state: AnimatedTextFieldState, duration: TimeInterval = 0) {
UIView.animate( UIView.animate(
withDuration: duration, withDuration: duration,
delay: 0, delay: 0,
@ -300,9 +348,15 @@ public class UIAnimatedTextField: UIView {
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
strongSelf.placeholderLabel.frame = strongSelf.placeholderLabelFrame(state: state) if strongSelf.isLeftTextAlignment {
strongSelf.placeholderLabel.transform = strongSelf.placeholderLabelTransform(state: state) strongSelf.placeholderLabel.transform = strongSelf.placeholderLabelTransform(state: state)
strongSelf.placeholderLabel.frame = strongSelf.placeholderLabelFrame(state: state)
} else {
strongSelf.placeholderLabel.frame = strongSelf.placeholderLabelFrame(state: state)
strongSelf.placeholderLabel.transform = strongSelf.placeholderLabelTransform(state: state)
}
switch state { switch state {
case .placeholder: case .placeholder:
strongSelf.placeholderLabel.textColor = strongSelf.placeholderBottomColor strongSelf.placeholderLabel.textColor = strongSelf.placeholderBottomColor
@ -312,50 +366,50 @@ public class UIAnimatedTextField: UIView {
}, },
completion: nil) completion: nil)
} }
// MARK: - Actions // MARK: - Actions
@objc private func tapGestureRecognizerAction(_ sender: UITapGestureRecognizer) { @objc private func tapGestureRecognizerAction(_ sender: UITapGestureRecognizer) {
delegate?.animatedTextFieldWillReactForTap?() delegate?.animatedTextFieldWillReactForTap?()
tapAction?(self) tapAction?(self)
} }
func validateText() -> Bool { open func validateText() -> Bool {
guard let text = textField.text else { guard let text = textField.text else {
return false return false
} }
switch type { switch type {
case .url: case .url:
return text.isValidEmail return text.isValidEmail
default: default:
break break
} }
return true return true
} }
func showInfo(infoText: String) { open func showInfo(infoText: String) {
guard !isShownInfo else { guard !isShownInfo else {
return return
} }
isShownInfo = true isShownInfo = true
let currentPlaceholder = placeholder let currentPlaceholder = placeholder
UIView.transition(with: placeholderLabel, UIView.transition(with: placeholderLabel,
duration: 0.5, duration: 0.5,
options: .transitionFlipFromTop, options: .transitionFlipFromTop,
animations: { [weak self] in animations: { [weak self] in
self?.placeholder = infoText self?.placeholder = infoText
}, completion: nil) }, completion: nil)
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2)) { [weak self] in DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2)) { [weak self] in
guard let placeholderLabel = self?.placeholderLabel else { guard let placeholderLabel = self?.placeholderLabel else {
return return
} }
UIView.transition(with: placeholderLabel, UIView.transition(with: placeholderLabel,
duration: 0.5, duration: 0.5,
options: .transitionFlipFromBottom, options: .transitionFlipFromBottom,
@ -366,118 +420,131 @@ public class UIAnimatedTextField: UIView {
}) })
} }
} }
// MARK: - Private // MARK: - Private
private func getDateInputView() -> UIDatePicker { private func getDateInputView() -> UIDatePicker {
let currentDate = Date() let currentDate = Date()
let datePicker = UIDatePicker() let datePicker = UIDatePicker()
datePicker.timeZone = TimeZone(secondsFromGMT: 0) datePicker.timeZone = TimeZone(secondsFromGMT: 0)
datePicker.datePickerMode = .date datePicker.datePickerMode = .date
datePicker.backgroundColor = UIColor.white datePicker.backgroundColor = UIColor.white
datePicker.setDate(currentDate, animated: true) datePicker.setDate(selectedDate ?? currentDate, animated: true)
datePicker.maximumDate = currentDate datePicker.maximumDate = currentDate
datePicker.addTarget(self, action: #selector(datePickerValueChanged(_:)), for: .valueChanged) datePicker.addTarget(self, action: #selector(datePickerValueChanged(_:)), for: .valueChanged)
return datePicker return datePicker
} }
@objc private func datePickerValueChanged(_ datePicker: UIDatePicker) { @objc private func datePickerValueChanged(_ datePicker: UIDatePicker) {
selectedDate = datePicker.date updateText(from: datePicker)
text = TIDateFormatter.longDate(from: datePicker.date)
} }
private func getDateInputAccessoryView() -> UIView { private func getDateInputAccessoryView() -> UIView {
let toolbar = UIToolbar() let toolbar = UIToolbar()
toolbar.frame = CGRect(x: 0, y: 0, width: bounds.width, height: 44) toolbar.frame = CGRect(x: 0, y: 0, width: bounds.width, height: 44)
toolbar.autoresizingMask = [.flexibleWidth] toolbar.autoresizingMask = [.flexibleWidth]
toolbar.barTintColor = UIColor.white toolbar.barTintColor = UIColor.white
let spacerItem = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) let spacerItem = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let doneItem = UIBarButtonItem(title: "Done", let doneItem = UIBarButtonItem(title: doneTitle,
style: .done, style: .done,
target: self, target: self,
action: #selector(datePickerDoneAction)) action: #selector(datePickerDoneAction))
let attributes = [ let attributes = [
NSForegroundColorAttributeName: UIColor.black NSAttributedString.Key.foregroundColor: doneTitleColor
] ]
doneItem.setTitleTextAttributes(attributes, for: .normal) doneItem.setTitleTextAttributes(attributes, for: .normal)
toolbar.items = [spacerItem, doneItem] toolbar.setItems([spacerItem, doneItem], animated: false)
return toolbar return toolbar
} }
fileprivate func updateText(from datePicker: UIDatePicker) {
selectedDate = datePicker.date
text = datePicker.date.toString(withFormat: dateFormat)
}
@objc private func datePickerDoneAction() { @objc private func datePickerDoneAction() {
if let datePicker = textField.inputView as? UIDatePicker {
updateText(from: datePicker)
}
textField.resignFirstResponder() textField.resignFirstResponder()
} }
@objc private func textFieldValueDidChange() { @objc private func textFieldValueDidChange() {
delegate?.animatedTextFieldValueDidChange?(self) delegate?.animatedTextFieldValueDidChange?(self)
} }
} }
// MARK: - Extension
extension UIAnimatedTextField: UITextFieldDelegate { extension UIAnimatedTextField: UITextFieldDelegate {
public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool { open func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
var result = true var result = true
if let delegateResult = delegate?.animatedTextFieldShouldBeginEditing?(self) { if let delegateResult = delegate?.animatedTextFieldShouldBeginEditing?(self) {
result = delegateResult result = delegateResult
} }
return result return result
} }
public func textFieldDidBeginEditing(_ textField: UITextField) { open func textFieldDidBeginEditing(_ textField: UITextField) {
if textField.text?.characters.count ?? 0 == 0 { if textField.text?.count ?? 0 == 0 {
setState(toState: .text, duration: UIAnimatedTextField.animationDuration) setState(toState: .text, duration: UIAnimatedTextField.animationDuration)
} }
if case .date = type { if case .date = type {
if let datePicker = textField.inputView as? UIDatePicker { if let datePicker = textField.inputView as? UIDatePicker {
textField.text = TIDateFormatter.longDate(from: datePicker.date) textField.text = datePicker.date.toString(withFormat: dateFormat)
} }
} }
delegate?.animatedTextFieldDidBeginEditing?(self) delegate?.animatedTextFieldDidBeginEditing?(self)
} }
public func textFieldShouldEndEditing(_ textField: UITextField) -> Bool { open func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
var result = true var result = true
if let delegateResult = delegate?.animatedTextFieldShouldEndEditing?(self) { if let delegateResult = delegate?.animatedTextFieldShouldEndEditing?(self) {
result = delegateResult result = delegateResult
} }
return result return result
} }
public func textFieldDidEndEditing(_ textField: UITextField) { open func textFieldDidEndEditing(_ textField: UITextField) {
if textField.text?.characters.count ?? 0 == 0 { if textField.text?.count ?? 0 == 0 {
setState(toState: .placeholder, duration: UIAnimatedTextField.animationDuration) setState(toState: .placeholder, duration: UIAnimatedTextField.animationDuration)
} }
if let datePicker = textField.inputView as? UIDatePicker {
updateText(from: datePicker)
}
delegate?.animatedTextFieldDidEndEditing?(self) delegate?.animatedTextFieldDidEndEditing?(self)
} }
public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, open func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange,
replacementString string: String) -> Bool { replacementString string: String) -> Bool {
var result = true var result = true
if let delegateResult = delegate?.animatedTextField?(self, shouldChangeCharactersInRange: range, if let delegateResult = delegate?.animatedTextField?(self, shouldChangeCharactersInRange: range,
replacementString: string) { replacementString: string) {
result = delegateResult result = delegateResult
} }
if string == " " { if string == Constants.space {
if textField.text?.characters.count ?? 0 == 0 { if textField.text?.isEmpty ?? true {
result = false result = false
} }
switch type { switch type {
case .password, .url: case .password, .url:
result = false result = false
@ -485,28 +552,28 @@ extension UIAnimatedTextField: UITextFieldDelegate {
break break
} }
} }
return result return result
} }
public func textFieldShouldClear(_ textField: UITextField) -> Bool { open func textFieldShouldClear(_ textField: UITextField) -> Bool {
var result = true var result = true
if let delegateResult = delegate?.animatedTextFieldShouldClear?(self) { if let delegateResult = delegate?.animatedTextFieldShouldClear?(self) {
result = delegateResult result = delegateResult
} }
return result return result
} }
public func textFieldShouldReturn(_ textField: UITextField) -> Bool { open func textFieldShouldReturn(_ textField: UITextField) -> Bool {
var result = true var result = true
if let delegateResult = delegate?.animatedTextFieldShouldReturn?(self) { if let delegateResult = delegate?.animatedTextFieldShouldReturn?(self) {
result = delegateResult result = delegateResult
} }
return result return result
} }
} }

View File

@ -1,9 +1,25 @@
// //
// UIView+Extensions.swift // UIView+Extensions.swift
// Pods
// //
// Created by Ivan Zinovyev on 12/19/16. // Copyright (c) 2016 Touch Instinct
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// //
import UIKit import UIKit

View File

@ -1 +0,0 @@
Example/Pods/Pods.xcodeproj