Compare commits

..

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

24 changed files with 275 additions and 533 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.3'
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,56 +1,17 @@
// //
// 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 { public class EditableTextField: UITextField {
case selectAll
case select var getType: (() -> TextType?)?
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(_:)),
@ -58,14 +19,8 @@ open class EditableTextField: UITextField {
#selector(copy(_:)), #selector(copy(_:)),
#selector(paste(_:)) #selector(paste(_:))
] ]
// MARK: - Overriden
override open func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { override public 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 public 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)
} }
@ -90,33 +45,5 @@ 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
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Created by Ivan Zinovyev on 12/19/16.
// 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,31 +1,15 @@
// //
// 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()
@objc optional func animatedTextFieldShouldBeginEditing(_ animatedTextField: UIAnimatedTextField) -> Bool @objc optional func animatedTextFieldShouldBeginEditing(_ animatedTextField: UIAnimatedTextField) -> Bool
@ -37,15 +21,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
} }
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) 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: - @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
} }
@ -130,8 +64,17 @@ open class UIAnimatedTextField: UIView {
layoutSubviews() layoutSubviews()
} }
} }
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
} }
@ -140,14 +83,25 @@ open class UIAnimatedTextField: UIView {
placeholderLabel.font = newValue placeholderLabel.font = newValue
} }
} }
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
@ -156,15 +110,15 @@ open 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
@ -178,7 +132,7 @@ open 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)
@ -193,55 +147,53 @@ open class UIAnimatedTextField: UIView {
} }
} }
} }
@objc public dynamic var selectedDate: Date? var selectedDate: Date?
public var dateFormat: String = Constants.defaultDateFormat
@IBInspectable public var placeholderTopColor: UIColor = UIColor.gray
public var doneTitle: String = Constants.done { @IBInspectable public var placeholderBottomColor: UIColor = UIColor.gray
didSet {
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
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
} }
return state return state
} }
// MARK: - Initialization // MARK: - Initialization
override public init(frame: CGRect) { override 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
@ -251,7 +203,7 @@ open 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
@ -259,50 +211,50 @@ open 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),
@ -311,7 +263,7 @@ open 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
@ -319,27 +271,27 @@ open class UIAnimatedTextField: UIView {
return CGAffineTransform(scaleX: 0.8, y: 0.8) return CGAffineTransform(scaleX: 0.8, y: 0.8)
} }
} }
override open func layoutSubviews() { override public 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
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,
@ -348,15 +300,9 @@ open class UIAnimatedTextField: UIView {
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
if strongSelf.isLeftTextAlignment { strongSelf.placeholderLabel.frame = strongSelf.placeholderLabelFrame(state: state)
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
@ -366,50 +312,50 @@ open 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)
} }
open func validateText() -> Bool { 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
} }
open func showInfo(infoText: String) { 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,
@ -420,131 +366,118 @@ open 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(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)
return datePicker return datePicker
} }
@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 {
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: 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()
} }
@objc private func textFieldValueDidChange() { @objc private func textFieldValueDidChange() {
delegate?.animatedTextFieldValueDidChange?(self) delegate?.animatedTextFieldValueDidChange?(self)
} }
} }
// 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) {
result = delegateResult result = delegateResult
} }
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) {
result = delegateResult result = delegateResult
} }
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
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 == Constants.space { if string == " " {
if textField.text?.isEmpty ?? true { if textField.text?.characters.count ?? 0 == 0 {
result = false result = false
} }
switch type { switch type {
case .password, .url: case .password, .url:
result = false result = false
@ -552,28 +485,28 @@ extension UIAnimatedTextField: UITextFieldDelegate {
break break
} }
} }
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) {
result = delegateResult result = delegateResult
} }
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) {
result = delegateResult result = delegateResult
} }
return result return result
} }
} }

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