Commit 7c2198ec by Daniel Dahan

development: progression with TextField

parent dfcc3fbd
...@@ -107,6 +107,7 @@ ...@@ -107,6 +107,7 @@
TargetAttributes = { TargetAttributes = {
96090AE31D9CDD2E00709CA6 = { 96090AE31D9CDD2E00709CA6 = {
CreatedOnToolsVersion = 8.0; CreatedOnToolsVersion = 8.0;
DevelopmentTeam = 9Z76XCNLGL;
ProvisioningStyle = Automatic; ProvisioningStyle = Automatic;
}; };
}; };
...@@ -262,7 +263,7 @@ ...@@ -262,7 +263,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = 9Z76XCNLGL;
INFOPLIST_FILE = TextField/Info.plist; INFOPLIST_FILE = TextField/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = io.cosmicmind.TextField; PRODUCT_BUNDLE_IDENTIFIER = io.cosmicmind.TextField;
...@@ -275,7 +276,7 @@ ...@@ -275,7 +276,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = 9Z76XCNLGL;
INFOPLIST_FILE = TextField/Info.plist; INFOPLIST_FILE = TextField/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = io.cosmicmind.TextField; PRODUCT_BUNDLE_IDENTIFIER = io.cosmicmind.TextField;
......
...@@ -40,9 +40,9 @@ class ViewController: UIViewController { ...@@ -40,9 +40,9 @@ class ViewController: UIViewController {
super.viewDidLoad() super.viewDidLoad()
view.backgroundColor = .white view.backgroundColor = .white
// prepareNameField() prepareNameField()
prepareEmailField() prepareEmailField()
// preparePasswordField() preparePasswordField()
prepareResignResponderButton() prepareResignResponderButton()
} }
...@@ -68,36 +68,39 @@ class ViewController: UIViewController { ...@@ -68,36 +68,39 @@ class ViewController: UIViewController {
} }
private func prepareNameField() { private func prepareNameField() {
let d: CGFloat = 40 let d: CGFloat = 32
nameField = TextField() nameField = TextField()
nameField.text = "Daniel Dahan" nameField.text = "Daniel Dahan"
nameField.placeholder = "Name" nameField.placeholder = "Name"
nameField.detail = "Your given name" nameField.detailLabel.text = "Your given name"
nameField.textAlignment = .center nameField.textAlignment = .center
nameField.clearButtonMode = .whileEditing nameField.clearButtonMode = .whileEditing
// Size the TextField to the maximum width, less 40 pixels on either side let leftView = UIImageView()
// with a top margin of 40 pixels. leftView.image = Icon.email?.tint(with: Color.cyan.base)
view.layout(nameField).top(d).horizontally(left: d, right: d)
nameField.leftView = leftView
nameField.leftViewMode = .always
view.layout(nameField).top(100).horizontally(left: d, right: d).height(d)
} }
private func prepareEmailField() { private func prepareEmailField() {
let d: CGFloat = 32 let d: CGFloat = 32
let leftView = UIImageView(frame: CGRect(x: 0, y: 0, width: d, height: d)) emailField = ErrorTextField(frame: CGRect(x: d, y: 200, width: view.width - (2 * d), height: d))
leftView.image = Icon.email?.tint(with: Color.cyan.base) emailField.placeholderLabel.text = "Email"
leftView.contentMode = .center emailField.detailLabel.text = "Error, incorrect email"
emailField = ErrorTextField(frame: CGRect(x: 40, y: 120, width: view.width - 80, height: d))
emailField.placeholder = "Email"
emailField.detail = "Error, incorrect email"
emailField.isClearIconButtonEnabled = true emailField.isClearIconButtonEnabled = true
emailField.delegate = self emailField.delegate = self
let leftView = UIImageView()
leftView.image = Icon.email?.tint(with: Color.cyan.base)
emailField.leftView = leftView emailField.leftView = leftView
emailField.leftViewMode = .always emailField.leftViewMode = .always
emailField.divider.contentEdgeInsets.left = d
// emailField.placeholderNormalColor = Color.amber.darken4 // emailField.placeholderNormalColor = Color.amber.darken4
// emailField.placeholderActiveColor = Color.pink.base // emailField.placeholderActiveColor = Color.pink.base
...@@ -107,20 +110,24 @@ class ViewController: UIViewController { ...@@ -107,20 +110,24 @@ class ViewController: UIViewController {
} }
private func preparePasswordField() { private func preparePasswordField() {
let d: CGFloat = 40 let d: CGFloat = 32
passwordField = TextField() passwordField = TextField()
passwordField.placeholder = "Password" passwordField.placeholderLabel.text = "Password"
passwordField.detail = "At least 8 characters" passwordField.detailLabel.text = "At least 8 characters"
passwordField.clearButtonMode = .whileEditing passwordField.clearButtonMode = .whileEditing
passwordField.isVisibilityIconButtonEnabled = true passwordField.isVisibilityIconButtonEnabled = true
let leftView = UIImageView()
leftView.image = Icon.email?.tint(with: Color.cyan.base)
passwordField.leftView = leftView
passwordField.leftViewMode = .always
// Setting the visibilityIconButton color. // Setting the visibilityIconButton color.
passwordField.visibilityIconButton?.tintColor = Color.green.base.withAlphaComponent(passwordField.isSecureTextEntry ? 0.38 : 0.54) passwordField.visibilityIconButton?.tintColor = Color.green.base.withAlphaComponent(passwordField.isSecureTextEntry ? 0.38 : 0.54)
// Size the TextField to the maximum width, less 40 pixels on either side view.layout(passwordField).top(300).horizontally(left: d, right: d).height(d)
// with a top margin of 200 pixels.
view.layout(passwordField).top(200).horizontally(left: d, right: d)
} }
} }
......
...@@ -42,6 +42,15 @@ public protocol SearchBarDelegate { ...@@ -42,6 +42,15 @@ public protocol SearchBarDelegate {
optional func searchBar(searchBar: SearchBar, didChange textField: UITextField, with text: String?) optional func searchBar(searchBar: SearchBar, didChange textField: UITextField, with text: String?)
/** /**
A delegation method that is executed when the textField will clear.
- Parameter searchBar: A SearchBar.
- Parameter willClear textField: A UITextField.
- Parameter with text: An optional String.
*/
@objc
optional func searchBar(searchBar: SearchBar, willClear textField: UITextField, with text: String?)
/**
A delegation method that is executed when the textField is cleared. A delegation method that is executed when the textField is cleared.
- Parameter searchBar: A SearchBar. - Parameter searchBar: A SearchBar.
- Parameter didClear textField: A UITextField. - Parameter didClear textField: A UITextField.
...@@ -175,8 +184,13 @@ open class SearchBar: Bar { ...@@ -175,8 +184,13 @@ open class SearchBar: Bar {
/// Clears the textField text. /// Clears the textField text.
@objc @objc
internal func handleClearButton() { internal func handleClearButton() {
delegate?.searchBar?(searchBar: self, didClear: textField, with: textField.text) let text = textField.text
delegate?.searchBar?(searchBar: self, willClear: textField, with: text)
textField.text = nil textField.text = nil
delegate?.searchBar?(searchBar: self, didClear: textField, with: text)
} }
// Live updates the search results. // Live updates the search results.
......
...@@ -32,7 +32,32 @@ import UIKit ...@@ -32,7 +32,32 @@ import UIKit
private var TextFieldContext: UInt8 = 0 private var TextFieldContext: UInt8 = 0
public protocol TextFieldDelegate: UITextFieldDelegate {} @objc(TextFieldDelegate)
public protocol TextFieldDelegate: UITextFieldDelegate {
/**
A delegation method that is executed when the textField changed.
- Parameter textField: A UITextField.
- Parameter didChange text: An optional String.
*/
@objc
optional func textField(textField: UITextField, didChange text: String?)
/**
A delegation method that is executed when the textField will clear.
- Parameter textField: A UITextField.
- Parameter willClear text: An optional String.
*/
@objc
optional func textField(textField: UITextField, willClear text: String?)
/**
A delegation method that is executed when the textField is cleared.
- Parameter textField: A UITextField.
- Parameter didClear text: An optional String.
*/
@objc
optional func textField(textField: UITextField, didClear text: String?)
}
open class TextField: UITextField { open class TextField: UITextField {
/// Default size when using AutoLayout. /// Default size when using AutoLayout.
...@@ -47,6 +72,14 @@ open class TextField: UITextField { ...@@ -47,6 +72,14 @@ open class TextField: UITextField {
/// A Boolean that indicates if the TextField is in an animating state. /// A Boolean that indicates if the TextField is in an animating state.
open internal(set) var isAnimating = false open internal(set) var isAnimating = false
/// The leftView width value.
open var leftViewWidth: CGFloat {
return leftViewOffset + height
}
/// The leftView width value.
open var leftViewOffset: CGFloat = 16
/// Divider normal height. /// Divider normal height.
@IBInspectable @IBInspectable
open var dividerNormalHeight: CGFloat = 1 open var dividerNormalHeight: CGFloat = 1
...@@ -112,32 +145,18 @@ open class TextField: UITextField { ...@@ -112,32 +145,18 @@ open class TextField: UITextField {
} }
set(value) { set(value) {
placeholderLabel.text = value placeholderLabel.text = value
guard let v = value else {
return
}
placeholderLabel.attributedText = NSAttributedString(string: v, attributes: [NSForegroundColorAttributeName: placeholderNormalColor])
} }
} }
/// The placeholder UILabel. /// The placeholder UILabel.
@IBInspectable @IBInspectable
open private(set) var placeholderLabel: UILabel! open private(set) lazy var placeholderLabel: UILabel = UILabel()
/// Placeholder normal text /// Placeholder normal text
@IBInspectable @IBInspectable
open var placeholderNormalColor = Color.darkText.others { open var placeholderNormalColor = Color.darkText.others {
didSet { didSet {
guard !isEditing else { updatePlaceholderLabelColor()
return
}
guard let v = placeholder else {
return
}
placeholderLabel.attributedText = NSAttributedString(string: v, attributes: [NSForegroundColorAttributeName: placeholderNormalColor])
} }
} }
...@@ -146,16 +165,7 @@ open class TextField: UITextField { ...@@ -146,16 +165,7 @@ open class TextField: UITextField {
open var placeholderActiveColor = Color.blue.base { open var placeholderActiveColor = Color.blue.base {
didSet { didSet {
tintColor = placeholderActiveColor tintColor = placeholderActiveColor
updatePlaceholderLabelColor()
guard isEditing else {
return
}
guard let v = placeholder else {
return
}
placeholderLabel.attributedText = NSAttributedString(string: v, attributes: [NSForegroundColorAttributeName: placeholderActiveColor])
} }
} }
...@@ -182,7 +192,7 @@ open class TextField: UITextField { ...@@ -182,7 +192,7 @@ open class TextField: UITextField {
@IBInspectable @IBInspectable
open var detailColor = Color.darkText.others { open var detailColor = Color.darkText.others {
didSet { didSet {
updateDetailLabelAttributedText() updateDetailLabelColor()
} }
} }
...@@ -225,11 +235,13 @@ open class TextField: UITextField { ...@@ -225,11 +235,13 @@ open class TextField: UITextField {
clearIconButton = IconButton(image: Icon.cm.clear, tintColor: placeholderNormalColor) clearIconButton = IconButton(image: Icon.cm.clear, tintColor: placeholderNormalColor)
clearIconButton!.contentEdgeInsets = .zero clearIconButton!.contentEdgeInsets = .zero
clearIconButton!.pulseAnimation = .center clearIconButton!.pulseAnimation = .none
clearButtonMode = .never clearButtonMode = .never
rightViewMode = .whileEditing rightViewMode = .whileEditing
rightView = clearIconButton rightView = clearIconButton
isClearIconButtonAutoHandled = isClearIconButtonAutoHandled ? true : false isClearIconButtonAutoHandled = isClearIconButtonAutoHandled ? true : false
layoutButton(button: clearIconButton)
} }
} }
...@@ -266,12 +278,14 @@ open class TextField: UITextField { ...@@ -266,12 +278,14 @@ open class TextField: UITextField {
visibilityIconButton = IconButton(image: Icon.visibility, tintColor: placeholderNormalColor.withAlphaComponent(isSecureTextEntry ? 0.38 : 0.54)) visibilityIconButton = IconButton(image: Icon.visibility, tintColor: placeholderNormalColor.withAlphaComponent(isSecureTextEntry ? 0.38 : 0.54))
visibilityIconButton!.contentEdgeInsets = .zero visibilityIconButton!.contentEdgeInsets = .zero
visibilityIconButton!.pulseAnimation = .center visibilityIconButton!.pulseAnimation = .none
isSecureTextEntry = true isSecureTextEntry = true
clearButtonMode = .never clearButtonMode = .never
rightViewMode = .whileEditing rightViewMode = .whileEditing
rightView = visibilityIconButton rightView = visibilityIconButton
isVisibilityIconButtonAutoHandled = isVisibilityIconButtonAutoHandled ? true : false isVisibilityIconButtonAutoHandled = isVisibilityIconButtonAutoHandled ? true : false
layoutButton(button: visibilityIconButton)
} }
} }
...@@ -307,17 +321,20 @@ open class TextField: UITextField { ...@@ -307,17 +321,20 @@ open class TextField: UITextField {
} }
open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard "detailLabel.text" == keyPath else { guard "placeholderLabel.text" == keyPath || "detailLabel.text" == keyPath else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
return return
} }
updateDetailLabelAttributedText() updatePlaceholderLabelColor()
layoutDetailLabel() updateDetailLabelColor()
layoutToSize()
} }
deinit { deinit {
removeObserver(self, forKeyPath: "detailLabel.text") removeObserver(self, forKeyPath: "detailLabel.text")
removeObserver(self, forKeyPath: "placeholderLabel.text")
} }
/** /**
...@@ -347,8 +364,13 @@ open class TextField: UITextField { ...@@ -347,8 +364,13 @@ open class TextField: UITextField {
open override func layoutSubviews() { open override func layoutSubviews() {
super.layoutSubviews() super.layoutSubviews()
layoutToSize()
layoutDivider() layoutDivider()
guard !isAnimating else {
return
}
layoutToSize()
} }
open override func layoutSublayers(of layer: CALayer) { open override func layoutSublayers(of layer: CALayer) {
...@@ -381,7 +403,13 @@ open class TextField: UITextField { ...@@ -381,7 +403,13 @@ open class TextField: UITextField {
return return
} }
let t = text
(delegate as? TextFieldDelegate)?.textField?(textField: self, willClear: t)
text = nil text = nil
(delegate as? TextFieldDelegate)?.textField?(textField: self, didClear: t)
} }
/// Handles the visibilityIconButton TouchUpInside event. /// Handles the visibilityIconButton TouchUpInside event.
...@@ -419,14 +447,11 @@ open class TextField: UITextField { ...@@ -419,14 +447,11 @@ open class TextField: UITextField {
/// Ensures that the components are sized correctly. /// Ensures that the components are sized correctly.
open func layoutToSize() { open func layoutToSize() {
guard !isAnimating else {
return
}
layoutPlaceholderLabel() layoutPlaceholderLabel()
layoutDetailLabel() layoutDetailLabel()
layoutButton(button: clearIconButton) layoutButton(button: clearIconButton)
layoutButton(button: visibilityIconButton) layoutButton(button: visibilityIconButton)
layoutLeftView()
} }
/// Layout the divider. /// Layout the divider.
...@@ -436,20 +461,19 @@ open class TextField: UITextField { ...@@ -436,20 +461,19 @@ open class TextField: UITextField {
/// Layout the placeholderLabel. /// Layout the placeholderLabel.
open func layoutPlaceholderLabel() { open func layoutPlaceholderLabel() {
let x = leftViewWidth
divider.contentEdgeInsets.left = x
if !isEditing && true == text?.isEmpty && isPlaceholderAnimated { if !isEditing && true == text?.isEmpty && isPlaceholderAnimated {
if let v = leftView?.width { placeholderLabel.frame = CGRect(x: x, y: bounds.origin.y, width: bounds.width - x, height: bounds.height)
placeholderLabel.frame = CGRect(x: v, y: bounds.origin.y, width: bounds.width - v, height: bounds.height)
} else {
placeholderLabel.frame = bounds
}
} else if placeholderLabel.transform.isIdentity { } else if placeholderLabel.transform.isIdentity {
placeholderLabel.frame = bounds placeholderLabel.frame = CGRect(x: x, y: bounds.origin.y, width: bounds.width - x, height: bounds.height)
placeholderLabel.transform = CGAffineTransform(scaleX: 0.75, y: 0.75) placeholderLabel.transform = CGAffineTransform(scaleX: 0.75, y: 0.75)
switch textAlignment { switch textAlignment {
case .left, .natural: case .left, .natural:
placeholderLabel.x = 0 placeholderLabel.x = x
case .right: case .right:
placeholderLabel.x = width - placeholderLabel.width placeholderLabel.x = width - placeholderLabel.width - x
default:break default:break
} }
placeholderLabel.y = -placeholderLabel.height + placeholderVerticalOffset placeholderLabel.y = -placeholderLabel.height + placeholderVerticalOffset
...@@ -457,25 +481,24 @@ open class TextField: UITextField { ...@@ -457,25 +481,24 @@ open class TextField: UITextField {
} else { } else {
switch textAlignment { switch textAlignment {
case .left, .natural: case .left, .natural:
placeholderLabel.x = 0 placeholderLabel.x = x
case .right: case .right:
placeholderLabel.x = width - placeholderLabel.width placeholderLabel.x = width - placeholderLabel.width - x
case .center: case .center:
placeholderLabel.center.x = width / 2 placeholderLabel.center.x = (width + x) / 2
default:break default:break
} }
placeholderLabel.width = width * 0.75 placeholderLabel.width = (width - x) * 0.75
} }
} }
/// Layout the detailLabel. /// Layout the detailLabel.
open func layoutDetailLabel() { open func layoutDetailLabel() {
guard let v = divider.line else { let c = divider.contentEdgeInsets
return detailLabel.sizeToFit()
} detailLabel.x = c.left
detailLabel.y = height + detailVerticalOffset
let h: CGFloat = nil == detail ? 12 : detailLabel.font.stringSize(string: detail!, constrainedToWidth: Double(width)).height detailLabel.width = width - c.left - c.right
detailLabel.frame = CGRect(x: 0, y: v.y + detailVerticalOffset, width: width, height: h)
} }
/// Layout the a button. /// Layout the a button.
...@@ -487,6 +510,17 @@ open class TextField: UITextField { ...@@ -487,6 +510,17 @@ open class TextField: UITextField {
button?.frame = CGRect(x: width - height, y: 0, width: height, height: height) button?.frame = CGRect(x: width - height, y: 0, width: height, height: height)
} }
/// Layout the leftView.
open func layoutLeftView() {
guard let v = leftView else {
return
}
v.width = leftViewWidth
v.height = height
v.contentMode = .center
}
/// The animation for the divider when editing begins. /// The animation for the divider when editing begins.
open func dividerEditingDidBeginAnimation() { open func dividerEditingDidBeginAnimation() {
dividerThickness = dividerActiveHeight dividerThickness = dividerActiveHeight
...@@ -518,13 +552,16 @@ open class TextField: UITextField { ...@@ -518,13 +552,16 @@ open class TextField: UITextField {
return return
} }
let x = s.leftViewWidth
s.divider.contentEdgeInsets.left = x
s.placeholderLabel.transform = CGAffineTransform(scaleX: 0.75, y: 0.75) s.placeholderLabel.transform = CGAffineTransform(scaleX: 0.75, y: 0.75)
switch s.textAlignment { switch s.textAlignment {
case .left, .natural: case .left, .natural:
s.placeholderLabel.x = 0 s.placeholderLabel.x = x
case .right: case .right:
s.placeholderLabel.x = s.width - s.placeholderLabel.width s.placeholderLabel.x = s.width - s.placeholderLabel.width - x
default:break default:break
} }
...@@ -548,8 +585,11 @@ open class TextField: UITextField { ...@@ -548,8 +585,11 @@ open class TextField: UITextField {
return return
} }
let x = s.leftViewWidth
s.divider.contentEdgeInsets.left = x
s.placeholderLabel.transform = CGAffineTransform.identity s.placeholderLabel.transform = CGAffineTransform.identity
s.placeholderLabel.x = s.leftView?.width ?? 0 s.placeholderLabel.x = x
s.placeholderLabel.y = 0 s.placeholderLabel.y = 0
s.placeholderLabel.textColor = s.placeholderNormalColor s.placeholderLabel.textColor = s.placeholderNormalColor
}) { [weak self] _ in }) { [weak self] _ in
...@@ -567,10 +607,10 @@ open class TextField: UITextField { ...@@ -567,10 +607,10 @@ open class TextField: UITextField {
/// Prepares the placeholderLabel. /// Prepares the placeholderLabel.
private func preparePlaceholderLabel() { private func preparePlaceholderLabel() {
placeholderLabel = UILabel()
placeholderNormalColor = Color.darkText.others placeholderNormalColor = Color.darkText.others
font = RobotoFont.regular(with: 16) font = RobotoFont.regular(with: 16)
addSubview(placeholderLabel) addSubview(placeholderLabel)
addObserver(self, forKeyPath: "placeholderLabel.text", options: [], context: &TextFieldContext)
} }
/// Prepares the detailLabel. /// Prepares the detailLabel.
...@@ -593,8 +633,17 @@ open class TextField: UITextField { ...@@ -593,8 +633,17 @@ open class TextField: UITextField {
textAlignment = .rightToLeft == UIApplication.shared.userInterfaceLayoutDirection ? .right : .left textAlignment = .rightToLeft == UIApplication.shared.userInterfaceLayoutDirection ? .right : .left
} }
/// Updates the placeholderLabel attributedText.
private func updatePlaceholderLabelColor() {
guard let v = placeholder else {
return
}
placeholderLabel.attributedText = NSAttributedString(string: v, attributes: [NSForegroundColorAttributeName: isEditing ? placeholderActiveColor : placeholderNormalColor])
}
/// Updates the detailLabel attributedText. /// Updates the detailLabel attributedText.
private func updateDetailLabelAttributedText() { private func updateDetailLabelColor() {
guard let v = detail else { guard let v = detail else {
return return
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment