Compare commits
60 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
51067b408f | |
|
|
b78e31424b | |
|
|
ee5ef07506 | |
|
|
2c186fb3de | |
|
|
aee22c2cef | |
|
|
cf9b3a72c5 | |
|
|
9bb955a4fe | |
|
|
9dea74461e | |
|
|
17ddd2c3b7 | |
|
|
7041eb4107 | |
|
|
7a0d673396 | |
|
|
c5bf32e147 | |
|
|
4adfe1489c | |
|
|
297ef68850 | |
|
|
cba697954b | |
|
|
de0e9adcab | |
|
|
352af583cc | |
|
|
1425bbe004 | |
|
|
11c69147ed | |
|
|
1d9541158e | |
|
|
c2a78d6b7b | |
|
|
720d0f404c | |
|
|
c635cd1688 | |
|
|
40ad393cbe | |
|
|
7883afcd74 | |
|
|
6924be8cee | |
|
|
ad91c59c5b | |
|
|
8278bf17e3 | |
|
|
e0c5ca347e | |
|
|
f5d6144142 | |
|
|
81141f5784 | |
|
|
651a930ac3 | |
|
|
808c549913 | |
|
|
1717e00407 | |
|
|
0117bb73e1 | |
|
|
5dcea5e0e1 | |
|
|
37edd38260 | |
|
|
afc41c735f | |
|
|
0c72961320 | |
|
|
61efe9044b | |
|
|
13e015ba5d | |
|
|
d983a40162 | |
|
|
6549e0689e | |
|
|
0c9dbea890 | |
|
|
0aeff35e5c | |
|
|
467797928e | |
|
|
3225462ea8 | |
|
|
0b8e6426bf | |
|
|
4008405d41 | |
|
|
6ede9f5e21 | |
|
|
341f6c600f | |
|
|
f3d0fe080f | |
|
|
914e6524ec | |
|
|
4f24e4f9cc | |
|
|
857d5b7bec | |
|
|
9dce982693 | |
|
|
779290d1d6 | |
|
|
222db00731 | |
|
|
4b10040099 | |
|
|
51dc2c6137 |
|
|
@ -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
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
4.0
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
## 0.2.0
|
||||||
|
|
||||||
|
- **Fix**: date setting after picker presented.
|
||||||
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
2
LICENSE
|
|
@ -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
|
||||||
|
|
|
||||||
93
README.md
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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'
|
||||||
|
|
|
||||||
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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(_:))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Example/Pods/Pods.xcodeproj
|
|
||||||