Compare commits

..

No commits in common. "master" and "0.1.0" have entirely different histories.

24 changed files with 278 additions and 536 deletions

3
.gitignore vendored
View File

@ -30,5 +30,4 @@ 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

View File

@ -1 +0,0 @@
4.0

View File

@ -1,5 +0,0 @@
# 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>"; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 12D59B30305289F1A0BAB148 /* UIAnimatedTextField.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = UIAnimatedTextField.podspec; path = ../UIAnimatedTextField.podspec; sourceTree = "<group>"; };
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,6 +110,7 @@
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 = (
); );
@ -127,12 +128,12 @@
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastSwiftUpdateCheck = 0720; LastSwiftUpdateCheck = 0720;
LastUpgradeCheck = 1000; LastUpgradeCheck = 0820;
ORGANIZATIONNAME = CocoaPods; ORGANIZATIONNAME = CocoaPods;
TargetAttributes = { TargetAttributes = {
607FACE41AFB9204008FA782 = { 607FACE41AFB9204008FA782 = {
CreatedOnToolsVersion = 6.3.1; CreatedOnToolsVersion = 6.3.1;
LastSwiftMigration = 0910; LastSwiftMigration = 0820;
TestTargetID = 607FACCF1AFB9204008FA782; TestTargetID = 607FACCF1AFB9204008FA782;
}; };
}; };
@ -172,34 +173,43 @@
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_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"; 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";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */
@ -224,22 +234,14 @@
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;
@ -279,22 +281,14 @@
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;
@ -332,8 +326,7 @@
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_SWIFT3_OBJC_INFERENCE = Default; SWIFT_VERSION = 3.0;
SWIFT_VERSION = 4.2;
}; };
name = Debug; name = Debug;
}; };
@ -346,8 +339,7 @@
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_SWIFT3_OBJC_INFERENCE = Default; SWIFT_VERSION = 3.0;
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 = "1000" LastUpgradeVersion = "0820"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"

View File

@ -1,10 +0,0 @@
<?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

@ -1,8 +0,0 @@
<?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 Touch Instinct Copyright (c) 2016 Ivan Zinovyev <ivan.zinovyev@touchin.ru>
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,99 +1,24 @@
# UIAnimatedTextField # UIAnimatedTextField
This custom control can be used as a replacement for UITextField. ## Example
When an user taps on it, a placeholder rises smoothly.
It comes with 5 different text types: simple, password, url, tappable, date. To run the example project, clone the repo, and run `pod install` from the Example directory first.
## 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 pod 'UIAnimatedTextField' and the source for podspecs to your Podfile. For example: it, simply add the following line to your Podfile:
```ruby ```ruby
source "https://github.com/iznv/Podspecs.git" pod "UIAnimatedTextField"
platform :ios, '9.0'
use_frameworks!
target "ProjectName" do
pod 'UIAnimatedTextField', '0.1.7'
end
``` ```
## Usage ## Author
Set height of UIView to 50 (optionally, to make UIAnimatedTextField look pretty). Create IBOutlet:
```swift
@IBOutlet weak var textField: UIAnimatedTextField!
```
In order to enable placeholder, set placeholder property: Ivan Zinovyev, ivan.zinovyev@touchin.ru
```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.3.0' s.version = '0.1.0'
s.summary = 'UITextField with animated placeholder' s.summary = 'UIAnimatedTextField'
s.description = <<-DESC s.description = <<-DESC
This custom control can be used as a replacement for UITextField. It comes with 5 different text types: simple, password, url, tappable, date. Animated text field
DESC DESC
s.homepage = 'https://github.com/TouchInstinct/UIAnimatedTextField' s.homepage = 'https://github.com/iznv/UIAnimatedTextField'
s.license = { :type => 'MIT', :file => 'LICENSE' } s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = "Touch Instinct" s.author = { 'Ivan Zinovyev' => 'ivan.zinovyev@touchin.ru' }
s.source = { :git => 'https://github.com/TouchInstinct/UIAnimatedTextField.git', :tag => s.version.to_s } s.source = { :git => 'https://github.com/iznv/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.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

View File

@ -1,33 +0,0 @@
//
// 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,55 +1,16 @@
// //
// EditableTextField.swift // EditableTextField.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
public enum EditableActionType { class EditableTextField: UITextField {
case selectAll
case select
case cut
case copy
case paste
public static let allActions: [EditableActionType] = [.selectAll, .select, .cut, .paste, .copy] var getType: (() -> TextType?)?
}
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(_:)),
@ -59,13 +20,7 @@ open class EditableTextField: UITextField {
#selector(paste(_:)) #selector(paste(_:))
] ]
// MARK: - Overriden override 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)
} }
@ -79,7 +34,7 @@ open class EditableTextField: UITextField {
return super.canPerformAction(action, withSender: sender) return super.canPerformAction(action, withSender: sender)
} }
override open func caretRect(for position: UITextPosition) -> CGRect { override 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)
} }
@ -91,32 +46,4 @@ open 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,31 +1,23 @@
// //
// 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

@ -0,0 +1,46 @@
//
// 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,30 +1,14 @@
// //
// UIAnimatedTextField.swift // UIAnimatedTextField.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 UIKit import UIKit
@objc public protocol UIAnimatedTextFieldDelegate: class { @objc protocol UIAnimatedTextFieldDelegate: class {
@objc optional func animatedTextFieldValueDidChange(_ animatedTextField: UIAnimatedTextField) @objc optional func animatedTextFieldValueDidChange(_ animatedTextField: UIAnimatedTextField)
@objc optional func animatedTextFieldWillReactForTap() @objc optional func animatedTextFieldWillReactForTap()
@ -40,12 +24,12 @@ import UIKit
} }
public enum AnimatedTextFieldState { enum AnimatedTextFieldState {
case placeholder case placeholder
case text case text
} }
public enum TextType { enum TextType {
case simple case simple
case password case password
case url case url
@ -54,73 +38,23 @@ public enum TextType {
} }
@IBDesignable @IBDesignable
open class UIAnimatedTextField: UIView { public 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 public var delegate: UIAnimatedTextFieldDelegate? weak var delegate: UIAnimatedTextFieldDelegate?
// MARK: - UI Properties // MARK: - UI Properties
private(set) public var textField: EditableTextField! private(set) var textField: EditableTextField!
private(set) public var placeholderLabel: UILabel! private(set) var placeholderLabel: UILabel!
private(set) public var lineView: UIView!
private(set) var lineView: UIView!
private var disclosureIndicatorImageView: UIImageView! private var disclosureIndicatorImageView: UIImageView!
// MARK: - @IBInspectable Properties // MARK: - Properties
@IBInspectable public var placeholder: String? { var text: String? {
get {
return placeholderLabel.text
}
set {
placeholderLabel.text = newValue
}
}
@IBInspectable public var isLeftTextAlignment: Bool {
get {
return textField.textAlignment == .left
}
set {
let alignment: NSTextAlignment = newValue ? .left : .center
textField.textAlignment = alignment
placeholderLabel.textAlignment = alignment
}
}
@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 { get {
return textField.text return textField.text
} }
@ -131,7 +65,16 @@ open class UIAnimatedTextField: UIView {
} }
} }
public var font: UIFont? { @IBInspectable var placeholder: String? {
get {
return placeholderLabel.text
}
set {
placeholderLabel.text = newValue
}
}
var font: UIFont? {
get { get {
return placeholderLabel.font return placeholderLabel.font
} }
@ -141,13 +84,24 @@ open class UIAnimatedTextField: UIView {
} }
} }
public var isDisclosureIndicatorVisible: Bool = false { var isDisclosureIndicatorVisible: Bool = false {
didSet { didSet {
disclosureIndicatorImageView.isHidden = !isDisclosureIndicatorVisible disclosureIndicatorImageView.isHidden = !isDisclosureIndicatorVisible
} }
} }
public var type: TextType = .simple { @IBInspectable var isLeftTextAlignment: Bool {
get {
return textField.textAlignment == .left
}
set {
let alignment: NSTextAlignment = newValue ? .left : .center
textField.textAlignment = alignment
placeholderLabel.textAlignment = alignment
}
}
var type: TextType = .simple {
didSet { didSet {
textField.isSecureTextEntry = false textField.isSecureTextEntry = false
textField.keyboardType = .default textField.keyboardType = .default
@ -194,28 +148,26 @@ open class UIAnimatedTextField: UIView {
} }
} }
@objc public dynamic var selectedDate: Date? var selectedDate: Date?
public var dateFormat: String = Constants.defaultDateFormat
public var doneTitle: String = Constants.done { @IBInspectable var placeholderTopColor: UIColor = UIColor.gray
didSet { @IBInspectable var placeholderBottomColor: UIColor = UIColor.gray
textField.inputAccessoryView = getDateInputAccessoryView()
} @IBInspectable var enteredTextColor: UIColor {
get { return textField.textColor ?? UIColor.black }
set { textField.textColor = newValue }
} }
public var doneTitleColor: UIColor = .black { @IBInspectable var lineColor: UIColor {
didSet { get { return lineView.backgroundColor ?? UIColor.gray }
textField.inputAccessoryView = getDateInputAccessoryView() set { lineView.backgroundColor = newValue }
} }
}
// 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
@ -223,7 +175,7 @@ open class UIAnimatedTextField: UIView {
private var state: AnimatedTextFieldState { private var state: AnimatedTextFieldState {
var state: AnimatedTextFieldState = .placeholder var state: AnimatedTextFieldState = .placeholder
if textField.text?.count ?? 0 > 0 || textField.isFirstResponder { if textField.text?.characters.count ?? 0 > 0 || textField.isFirstResponder {
state = .text state = .text
} }
@ -232,7 +184,7 @@ open class UIAnimatedTextField: UIView {
// MARK: - Initialization // MARK: - Initialization
override public init(frame: CGRect) { override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
initialization() initialization()
} }
@ -320,7 +272,7 @@ open class UIAnimatedTextField: UIView {
} }
} }
override open func layoutSubviews() { override public func layoutSubviews() {
super.layoutSubviews() super.layoutSubviews()
textField.frame = textFieldFrame() textField.frame = textFieldFrame()
@ -339,7 +291,7 @@ open class UIAnimatedTextField: UIView {
// MARK: - Animation // MARK: - Animation
public func setState(toState state: AnimatedTextFieldState, duration: TimeInterval = 0) { func setState(toState state: AnimatedTextFieldState, duration: TimeInterval) {
UIView.animate( UIView.animate(
withDuration: duration, withDuration: duration,
delay: 0, delay: 0,
@ -349,14 +301,8 @@ open class UIAnimatedTextField: UIView {
return return
} }
if strongSelf.isLeftTextAlignment {
strongSelf.placeholderLabel.transform = strongSelf.placeholderLabelTransform(state: state)
strongSelf.placeholderLabel.frame = strongSelf.placeholderLabelFrame(state: state)
} else {
strongSelf.placeholderLabel.frame = strongSelf.placeholderLabelFrame(state: state) strongSelf.placeholderLabel.frame = strongSelf.placeholderLabelFrame(state: state)
strongSelf.placeholderLabel.transform = strongSelf.placeholderLabelTransform(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
@ -374,7 +320,7 @@ open class UIAnimatedTextField: UIView {
tapAction?(self) tapAction?(self)
} }
open func validateText() -> Bool { func validateText() -> Bool {
guard let text = textField.text else { guard let text = textField.text else {
return false return false
} }
@ -389,7 +335,7 @@ open class UIAnimatedTextField: UIView {
return true return true
} }
open func showInfo(infoText: String) { func showInfo(infoText: String) {
guard !isShownInfo else { guard !isShownInfo else {
return return
} }
@ -430,7 +376,7 @@ open class UIAnimatedTextField: UIView {
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(selectedDate ?? currentDate, animated: true) datePicker.setDate(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)
@ -438,7 +384,8 @@ open class UIAnimatedTextField: UIView {
} }
@objc private func datePickerValueChanged(_ datePicker: UIDatePicker) { @objc private func datePickerValueChanged(_ datePicker: UIDatePicker) {
updateText(from: datePicker) selectedDate = datePicker.date
text = TIDateFormatter.longDate(from: datePicker.date)
} }
private func getDateInputAccessoryView() -> UIView { private func getDateInputAccessoryView() -> UIView {
@ -448,30 +395,22 @@ open class UIAnimatedTextField: UIView {
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: doneTitle, let doneItem = UIBarButtonItem(title: "Done",
style: .done, style: .done,
target: self, target: self,
action: #selector(datePickerDoneAction)) action: #selector(datePickerDoneAction))
let attributes = [ let attributes = [
NSAttributedString.Key.foregroundColor: doneTitleColor NSForegroundColorAttributeName: UIColor.black
] ]
doneItem.setTitleTextAttributes(attributes, for: .normal) doneItem.setTitleTextAttributes(attributes, for: .normal)
toolbar.setItems([spacerItem, doneItem], animated: false) toolbar.items = [spacerItem, doneItem]
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()
} }
@ -481,11 +420,9 @@ open class UIAnimatedTextField: UIView {
} }
// MARK: - Extension
extension UIAnimatedTextField: UITextFieldDelegate { extension UIAnimatedTextField: UITextFieldDelegate {
open func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool { public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
var result = true var result = true
if let delegateResult = delegate?.animatedTextFieldShouldBeginEditing?(self) { if let delegateResult = delegate?.animatedTextFieldShouldBeginEditing?(self) {
@ -495,21 +432,21 @@ extension UIAnimatedTextField: UITextFieldDelegate {
return result return result
} }
open func textFieldDidBeginEditing(_ textField: UITextField) { public func textFieldDidBeginEditing(_ textField: UITextField) {
if textField.text?.count ?? 0 == 0 { if textField.text?.characters.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 = datePicker.date.toString(withFormat: dateFormat) textField.text = TIDateFormatter.longDate(from: datePicker.date)
} }
} }
delegate?.animatedTextFieldDidBeginEditing?(self) delegate?.animatedTextFieldDidBeginEditing?(self)
} }
open func textFieldShouldEndEditing(_ textField: UITextField) -> Bool { public func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
var result = true var result = true
if let delegateResult = delegate?.animatedTextFieldShouldEndEditing?(self) { if let delegateResult = delegate?.animatedTextFieldShouldEndEditing?(self) {
@ -519,19 +456,15 @@ extension UIAnimatedTextField: UITextFieldDelegate {
return result return result
} }
open func textFieldDidEndEditing(_ textField: UITextField) { public func textFieldDidEndEditing(_ textField: UITextField) {
if textField.text?.count ?? 0 == 0 { if textField.text?.characters.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)
} }
open func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange,
replacementString string: String) -> Bool { replacementString string: String) -> Bool {
var result = true var result = true
@ -540,8 +473,8 @@ extension UIAnimatedTextField: UITextFieldDelegate {
result = delegateResult result = delegateResult
} }
if string == Constants.space { if string == " " {
if textField.text?.isEmpty ?? true { if textField.text?.characters.count ?? 0 == 0 {
result = false result = false
} }
@ -556,7 +489,7 @@ extension UIAnimatedTextField: UITextFieldDelegate {
return result return result
} }
open func textFieldShouldClear(_ textField: UITextField) -> Bool { public func textFieldShouldClear(_ textField: UITextField) -> Bool {
var result = true var result = true
if let delegateResult = delegate?.animatedTextFieldShouldClear?(self) { if let delegateResult = delegate?.animatedTextFieldShouldClear?(self) {
@ -566,7 +499,7 @@ extension UIAnimatedTextField: UITextFieldDelegate {
return result return result
} }
open func textFieldShouldReturn(_ textField: UITextField) -> Bool { public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
var result = true var result = true
if let delegateResult = delegate?.animatedTextFieldShouldReturn?(self) { if let delegateResult = delegate?.animatedTextFieldShouldReturn?(self) {

View File

@ -1,25 +1,9 @@
// //
// UIView+Extensions.swift // UIView+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 UIKit import UIKit

1
_Pods.xcodeproj Symbolic link
View File

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