diff --git a/KSTokenView.podspec b/KSTokenView.podspec
index 43dd357..8756edb 100644
--- a/KSTokenView.podspec
+++ b/KSTokenView.podspec
@@ -21,7 +21,7 @@ Pod::Spec.new do |s|
# ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
- s.ios.deployment_target = '9.0'
+ s.ios.deployment_target = '8.0'
# ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
diff --git a/KSTokenView/KSToken.swift b/KSTokenView/KSToken.swift
index 5a5e7b5..f3383fc 100644
--- a/KSTokenView/KSToken.swift
+++ b/KSTokenView/KSToken.swift
@@ -29,164 +29,164 @@ import UIKit
//__________________________________________________________________________________
//
open class KSToken : UIControl {
-
- //MARK: - Public Properties
- //__________________________________________________________________________________
- //
-
- /// retuns title as description
- override open var description : String {
- get {
- return title
+
+ //MARK: - Public Properties
+ //__________________________________________________________________________________
+ //
+
+ /// retuns title as description
+ override open var description : String {
+ get {
+ return title
+ }
+ }
+
+ /// default is ""
+ open var title = ""
+
+ /// default is nil. Any Custom object.
+ open var object: AnyObject?
+
+ /// default is false. If set to true, token can not be deleted
+ open var sticky = false
+
+ /// default is 15
+ open var tokenCornerRadius:CGFloat = 15.0
+
+ /// Token Title color
+ open var tokenTextColor = UIColor(red: 255/255, green: 255/255, blue: 255/255, alpha: 1)
+
+ /// Token background color
+ open var tokenBackgroundColor = UIColor(red: 50/255, green: 50/255, blue: 255/255, alpha: 1)
+
+ /// Token title color in selected state
+ open var tokenTextHighlightedColor: UIColor?
+
+ /// Token backgrould color in selected state
+ open var tokenBackgroundHighlightedColor: UIColor?
+
+ /// Token background color in selected state. It doesn't have effect if 'tokenBackgroundHighlightedColor' is set
+ open var darkRatio: CGFloat = 0.75
+
+ /// Token border width
+ open var borderWidth: CGFloat = 0.0
+
+ ///Token border color
+ open var borderColor: UIColor = UIColor.black
+
+ /// default is 200. Maximum width of token. After maximum limit is reached title is truncated at end with '...'
+ fileprivate var _maxWidth: CGFloat? = 200
+ open var maxWidth: CGFloat {
+ get{
+ return _maxWidth!
+ }
+ set (newWidth) {
+ if (_maxWidth != newWidth) {
+ _maxWidth = newWidth
+ sizeToFit()
+ setNeedsDisplay()
}
- }
-
- /// default is ""
- open var title = ""
-
- /// default is nil. Any Custom object.
- open var object: AnyObject?
-
- /// default is false. If set to true, token can not be deleted
- open var sticky = false
-
- /// Token Title color
- open var tokenTextColor = UIColor(red: 255/255, green: 255/255, blue: 255/255, alpha: 1)
-
- /// Token background color
- open var tokenBackgroundColor = UIColor(red: 50/255, green: 50/255, blue: 255/255, alpha: 1)
-
- /// Token title color in selected state
- open var tokenTextHighlightedColor: UIColor?
-
- /// Token backgrould color in selected state
- open var tokenBackgroundHighlightedColor: UIColor?
-
- /// Token background color in selected state. It doesn't have effect if 'tokenBackgroundHighlightedColor' is set
- open var darkRatio: CGFloat = 0.75
-
- /// Token border width
- open var borderWidth: CGFloat = 0.0
-
- ///Token border color
- open var borderColor: UIColor = UIColor.black
-
- /// default is 200. Maximum width of token. After maximum limit is reached title is truncated at end with '...'
- fileprivate var _maxWidth: CGFloat? = 200
- open var maxWidth: CGFloat {
- get{
- return _maxWidth!
- }
- set (newWidth) {
- if (_maxWidth != newWidth) {
- _maxWidth = newWidth
- sizeToFit()
- setNeedsDisplay()
- }
- }
- }
-
- /// returns true if token is selected
- override open var isSelected: Bool {
- didSet (newValue) {
- setNeedsDisplay()
- }
- }
-
- //MARK: - Constructors
- //__________________________________________________________________________________
- //
- convenience required public init(coder aDecoder: NSCoder) {
- self.init(title: "")
- }
-
- convenience public init(title: String) {
- self.init(title: title, object: title as AnyObject?);
- }
-
- public init(title: String, object: AnyObject?) {
- self.title = title
- self.object = object
- super.init(frame: CGRect.zero)
- backgroundColor = UIColor.clear
- }
-
- //MARK: - Drawing code
- //__________________________________________________________________________________
- //
- override open func draw(_ rect: CGRect) {
- //// General Declarations
- let context = UIGraphicsGetCurrentContext()
-
- //// Rectangle Drawing
-
- // fill background
- let rectanglePath = UIBezierPath(roundedRect: rect, cornerRadius: 15)
-
- var textColor: UIColor
- var backgroundColor: UIColor
-
- if (isSelected) {
- if (tokenBackgroundHighlightedColor != nil) {
- backgroundColor = tokenBackgroundHighlightedColor!
- } else {
- backgroundColor = tokenBackgroundColor.darkendColor(darkRatio)
- }
-
- if (tokenTextHighlightedColor != nil) {
- textColor = tokenTextHighlightedColor!
- } else {
- textColor = tokenTextColor
- }
-
+ }
+ }
+
+ /// returns true if token is selected
+ override open var isSelected: Bool {
+ didSet (newValue) {
+ setNeedsDisplay()
+ }
+ }
+
+ //MARK: - Constructors
+ //__________________________________________________________________________________
+ //
+ convenience required public init(coder aDecoder: NSCoder) {
+ self.init(title: "")
+ }
+
+ convenience public init(title: String) {
+ self.init(title: title, object: title as AnyObject?);
+ }
+
+ public init(title: String, object: AnyObject?) {
+ self.title = title
+ self.object = object
+ super.init(frame: CGRect.zero)
+ backgroundColor = UIColor.clear
+ }
+
+ //MARK: - Drawing code
+ //__________________________________________________________________________________
+ //
+ override open func draw(_ rect: CGRect) {
+ //// General Declarations
+ let context = UIGraphicsGetCurrentContext()
+
+ //// Rectangle Drawing
+
+ // fill background
+ let rectanglePath = UIBezierPath(roundedRect: rect, cornerRadius: tokenCornerRadius)
+
+ var textColor: UIColor = tokenTextColor
+ var backgroundColor: UIColor
+
+ if (isSelected) {
+ if (tokenBackgroundHighlightedColor != nil) {
+ backgroundColor = tokenBackgroundHighlightedColor!
} else {
- backgroundColor = tokenBackgroundColor
- textColor = tokenTextColor
- }
-
- backgroundColor.setFill()
- rectanglePath.fill()
-
- var paddingX: CGFloat = 0.0
- var font = UIFont.systemFont(ofSize: 14)
- var tokenField: KSTokenField? {
- return superview! as? KSTokenField
- }
- if ((tokenField) != nil) {
- paddingX = tokenField!.paddingX()!
- font = tokenField!.tokenFont()!
+ backgroundColor = tokenBackgroundColor.darkendColor(darkRatio)
}
- // Text
- let rectangleTextContent = title
- let rectangleStyle = NSMutableParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle
- rectangleStyle.lineBreakMode = NSLineBreakMode.byTruncatingTail
- rectangleStyle.alignment = NSTextAlignment.center
- let rectangleFontAttributes = [NSFontAttributeName: font, NSForegroundColorAttributeName: textColor, NSParagraphStyleAttributeName: rectangleStyle] as [String : Any]
-
- let maxDrawableHeight = max(rect.height , font.lineHeight)
- let textHeight: CGFloat = KSUtils.getRect(rectangleTextContent as NSString, width: rect.width, height: maxDrawableHeight , font: font).size.height
-
-
- let textRect = CGRect(x: rect.minX + paddingX, y: rect.minY + (maxDrawableHeight - textHeight) / 2, width: min(maxWidth, rect.width) - (paddingX*2), height: maxDrawableHeight)
-
- rectangleTextContent.draw(in: textRect, withAttributes: rectangleFontAttributes)
-
- #if swift(>=2.3)
- context!.saveGState()
- context!.clip(to: rect)
- context!.restoreGState()
- #else
- context.saveGState()
- context.clip(to: rect)
- context.restoreGState()
- #endif
-
- // Border
- if (borderWidth > 0.0 && borderColor != UIColor.clear) {
- borderColor.setStroke()
- rectanglePath.lineWidth = borderWidth
- rectanglePath.stroke()
+ if (tokenTextHighlightedColor != nil) {
+ textColor = tokenTextHighlightedColor!
+ } else {
+ textColor = tokenTextColor
}
- }
+ } else {
+ backgroundColor = tokenBackgroundColor.darkendColor(darkRatio)
+ }
+ backgroundColor.setFill()
+ rectanglePath.fill()
+
+ var paddingX: CGFloat = 0.0
+ var font = UIFont.systemFont(ofSize: 14)
+ var tokenField: KSTokenField? {
+ return superview! as? KSTokenField
+ }
+ if ((tokenField) != nil) {
+ paddingX = tokenField!.paddingX()!
+ font = tokenField!.tokenFont()!
+ }
+
+ // Text
+ let rectangleTextContent = title
+ let rectangleStyle = NSMutableParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle
+ rectangleStyle.lineBreakMode = NSLineBreakMode.byTruncatingTail
+ rectangleStyle.alignment = NSTextAlignment.center
+ let rectangleFontAttributes = [NSFontAttributeName: font, NSForegroundColorAttributeName: textColor, NSParagraphStyleAttributeName: rectangleStyle] as [String : Any]
+
+ let maxDrawableHeight = max(rect.height , font.lineHeight)
+ let textHeight: CGFloat = KSUtils.getRect(rectangleTextContent as NSString, width: rect.width, height: maxDrawableHeight , font: font).size.height
+
+
+ let textRect = CGRect(x: rect.minX + paddingX, y: rect.minY + (maxDrawableHeight - textHeight) / 2, width: min(maxWidth, rect.width) - (paddingX*2), height: maxDrawableHeight)
+
+ rectangleTextContent.draw(in: textRect, withAttributes: rectangleFontAttributes)
+
+ #if swift(>=2.3)
+ context!.saveGState()
+ context!.clip(to: rect)
+ context!.restoreGState()
+ #else
+ context.saveGState()
+ context.clip(to: rect)
+ context.restoreGState()
+ #endif
+
+ // Border
+ if (borderWidth > 0.0 && borderColor != UIColor.clear) {
+ borderColor.setStroke()
+ rectanglePath.lineWidth = borderWidth
+ rectanglePath.stroke()
+ }
+ }
}
diff --git a/KSTokenView/KSTokenField.swift b/KSTokenView/KSTokenField.swift
old mode 100644
new mode 100755
index 59ae619..6e6cf18
--- a/KSTokenView/KSTokenField.swift
+++ b/KSTokenView/KSTokenField.swift
@@ -25,197 +25,203 @@
import UIKit
enum KSTokenFieldState {
- case opened
- case closed
+ case opened
+ case closed
}
@objc protocol KSTokenFieldDelegate : UITextFieldDelegate {
- func tokenFieldShouldChangeHeight(_ height: CGFloat)
- @objc optional func tokenFieldDidSelectToken(_ token: KSToken)
- @objc optional func tokenFieldDidBeginEditing(_ tokenField: KSTokenField)
- @objc optional func tokenFieldDidEndEditing(_ tokenField: KSTokenField)
+ func tokenFieldShouldChangeHeight(_ height: CGFloat)
+ @objc optional func tokenFieldDidSelectToken(_ token: KSToken)
+ @objc optional func tokenFieldDidBeginEditing(_ tokenField: KSTokenField)
+ @objc optional func tokenFieldDidEndEditing(_ tokenField: KSTokenField)
}
open class KSTokenField: UITextField {
-
- // MARK: - Private Properties
- fileprivate var _cursorColor: UIColor = UIColor.gray {
- willSet {
- tintColor = newValue
- }
- }
- fileprivate var _setupCompleted: Bool = false
- fileprivate var _selfFrame: CGRect?
- fileprivate var _caretPoint: CGPoint?
- fileprivate var _placeholderValue: String?
- fileprivate var _placeholderLabel: UILabel?
- fileprivate var _state: KSTokenFieldState = .opened
- fileprivate var _minWidthForInput: CGFloat = 50.0
- fileprivate var _separatorText: String?
- fileprivate var _font: UIFont?
- fileprivate var _paddingX: CGFloat?
- fileprivate var _paddingY: CGFloat?
- fileprivate var _marginX: CGFloat?
- fileprivate var _marginY: CGFloat?
- fileprivate var _bufferX: CGFloat?
- fileprivate var _removesTokensOnEndEditing = true
- fileprivate var _scrollView = UIScrollView(frame: .zero)
- fileprivate var _scrollPoint = CGPoint.zero
- fileprivate var _direction: KSTokenViewScrollDirection = .vertical {
- didSet {
- if (oldValue != _direction) {
- updateLayout()
- }
- }
- }
- fileprivate var _descriptionText: String = "selections" {
- didSet {
- _updateText()
- }
- }
-
- // MARK: - Public Properties
-
- /// default is grayColor()
- var promptTextColor: UIColor = UIColor.gray
-
- /// default is grayColor()
- var placeHolderColor: UIColor = UIColor.gray
-
- /// default is 120.0. After maximum limit is reached, tokens starts scrolling vertically
- var maximumHeight: CGFloat = 120.0
-
- /// default is nil
- override open var placeholder: String? {
- get {
- return _placeholderValue
- }
- set {
- super.placeholder = newValue
- if (newValue == nil) {
- return
- }
- _placeholderValue = newValue
- }
- }
-
- weak var parentView: KSTokenView? {
- willSet (tokenView) {
- if (tokenView != nil) {
- _cursorColor = tokenView!.cursorColor
- _paddingX = tokenView!.paddingX
- _paddingY = tokenView!.paddingY
- _marginX = tokenView!.marginX
- _marginY = tokenView!.marginY
- _bufferX = tokenView!.bufferX
- _direction = tokenView!.direction
- _font = tokenView!.font
- if (_font != nil) {
- font = _font
- }
- _minWidthForInput = tokenView!.minWidthForInput
- _separatorText = tokenView!.separatorText
- _removesTokensOnEndEditing = tokenView!.removesTokensOnEndEditing
- _descriptionText = tokenView!.descriptionText
- placeHolderColor = tokenView!.placeholderColor
- promptTextColor = tokenView!.promptColor
- _setPromptText(tokenView!.promptText)
-
- if (_setupCompleted) {
- updateLayout()
- }
- }
- }
- }
-
- weak var tokenFieldDelegate: KSTokenFieldDelegate? {
- didSet {
- delegate = tokenFieldDelegate
- }
- }
-
- /// returns Array of tokens
- var tokens = [KSToken]()
-
- /// returns selected KSToken object
- var selectedToken: KSToken?
-
- // MARK: - Constructors
- required public init?(coder aDecoder: NSCoder) {
- super.init(coder: aDecoder)
- _setupTokenField()
- }
-
- override init(frame: CGRect) {
- super.init(frame: frame)
- _setupTokenField()
- }
-
-
- // MARK: - Methods
-
- // MARK: - Setup
- fileprivate func _setupTokenField() {
- text = ""
- autocorrectionType = UITextAutocorrectionType.no
- autocapitalizationType = UITextAutocapitalizationType.none
- contentVerticalAlignment = UIControlContentVerticalAlignment.top
- returnKeyType = UIReturnKeyType.done
- text = KSTextEmpty
- backgroundColor = UIColor.white
- clipsToBounds = true
- _state = .closed
-
- _setScrollRect()
- _scrollView.backgroundColor = UIColor.clear
- _scrollView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
- let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(UIResponder.becomeFirstResponder))
- gestureRecognizer.cancelsTouchesInView = false
- _scrollView.addGestureRecognizer(gestureRecognizer)
- _scrollView.delegate = self
- addSubview(_scrollView)
-
- addTarget(self, action: #selector(KSTokenField.tokenFieldTextDidChange(_:)), for: UIControlEvents.editingChanged)
- }
-
- fileprivate func _setScrollRect() {
+
+ // MARK: - Private Properties
+ fileprivate var _cursorColor: UIColor = UIColor.gray {
+ willSet {
+ tintColor = newValue
+ }
+ }
+ fileprivate var _setupCompleted: Bool = false
+ fileprivate var _selfFrame: CGRect?
+ fileprivate var _caretPoint: CGPoint?
+ fileprivate var _placeholderValue: String?
+ fileprivate var _placeholderLabel: UILabel?
+ fileprivate var _state: KSTokenFieldState = .opened
+ fileprivate var _minWidthForInput: CGFloat = 50.0
+ fileprivate var _separatorText: String?
+ fileprivate var _font: UIFont?
+ fileprivate var _paddingX: CGFloat?
+ fileprivate var _paddingY: CGFloat?
+ fileprivate var _marginX: CGFloat?
+ fileprivate var _marginY: CGFloat?
+ fileprivate var _bufferX: CGFloat?
+ fileprivate var _removesTokensOnEndEditing = true
+ fileprivate var _scrollView = UIScrollView(frame: .zero)
+ fileprivate var _scrollPoint = CGPoint.zero
+ fileprivate var _direction: KSTokenViewScrollDirection = .vertical {
+ didSet {
+ if (oldValue != _direction) {
+ updateLayout()
+ }
+ }
+ }
+ fileprivate var _descriptionText: String = "selections" {
+ didSet {
+ _updateText()
+ }
+ }
+
+ // MARK: - Public Properties
+
+ /// default is grayColor()
+ var promptTextColor: UIColor = UIColor.gray
+
+ /// default is grayColor()
+ var placeHolderColor: UIColor = UIColor.gray
+
+ /// default is 120.0. After maximum limit is reached, tokens starts scrolling vertically
+ var maximumHeight: CGFloat = 120.0
+
+ /// default is nil
+ override open var placeholder: String? {
+ get {
+ return _placeholderValue
+ }
+ set {
+ super.placeholder = newValue
+ if (newValue == nil) {
+ return
+ }
+ _placeholderValue = newValue
+ }
+ }
+
+ weak var parentView: KSTokenView? {
+ willSet (tokenView) {
+ if (tokenView != nil) {
+ _cursorColor = tokenView!.cursorColor
+ _paddingX = tokenView!.paddingX
+ _paddingY = tokenView!.paddingY
+ _marginX = tokenView!.marginX
+ _marginY = tokenView!.marginY
+ _bufferX = tokenView!.bufferX
+ _direction = tokenView!.direction
+ _font = tokenView!.font
+ if (_font != nil) {
+ font = _font
+ }
+ _minWidthForInput = tokenView!.minWidthForInput
+ _separatorText = tokenView!.separatorText
+ _removesTokensOnEndEditing = tokenView!.removesTokensOnEndEditing
+ _descriptionText = tokenView!.descriptionText
+ placeHolderColor = tokenView!.placeholderColor
+ promptTextColor = tokenView!.promptColor
+ _setPromptText(tokenView!.promptText)
+
+ if (_setupCompleted) {
+ updateLayout()
+ }
+ }
+ }
+ }
+
+ weak var tokenFieldDelegate: KSTokenFieldDelegate? {
+ didSet {
+ delegate = tokenFieldDelegate
+ }
+ }
+
+ /// returns Array of tokens
+ var tokens = [KSToken]()
+
+ /// returns selected KSToken object
+ var selectedToken: KSToken?
+
+ var scrollViewHeight: CGFloat {
+ get {
+ return _scrollView.frame.height
+ }
+ }
+
+ // MARK: - Constructors
+ required public init?(coder aDecoder: NSCoder) {
+ super.init(coder: aDecoder)
+ _setupTokenField()
+ }
+
+ override init(frame: CGRect) {
+ super.init(frame: frame)
+ _setupTokenField()
+ }
+
+
+ // MARK: - Methods
+
+ // MARK: - Setup
+ fileprivate func _setupTokenField() {
+ text = ""
+ autocorrectionType = UITextAutocorrectionType.no
+ autocapitalizationType = UITextAutocapitalizationType.none
+ contentVerticalAlignment = UIControlContentVerticalAlignment.top
+ returnKeyType = UIReturnKeyType.done
+ text = KSTextEmpty
+ backgroundColor = UIColor.white
+ clipsToBounds = true
+ _state = .closed
+
+ _setScrollRect()
+ _scrollView.backgroundColor = UIColor.clear
+ _scrollView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
+ let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(UIResponder.becomeFirstResponder))
+ gestureRecognizer.cancelsTouchesInView = false
+ _scrollView.addGestureRecognizer(gestureRecognizer)
+ _scrollView.delegate = self
+ addSubview(_scrollView)
+
+ addTarget(self, action: #selector(KSTokenField.tokenFieldTextDidChange(_:)), for: UIControlEvents.editingChanged)
+ }
+
+ fileprivate func _setScrollRect() {
let buffer:CGFloat = _bufferX ?? 0.0;
let width = frame.width - _leftViewRect().width
let height = frame.height
_scrollView.frame = CGRect(x: _leftViewRect().width + buffer, y: 0, width: width, height: height)
-// _scrollView.setContentOffset(CGPoint(x: 0, y: 0), animated: false)
-// _scrollView.contentSize = CGSize(width: width, height: height)
- }
-
- override open func draw(_ rect: CGRect) {
- _selfFrame = rect
- _setupCompleted = true
- _updateText()
-
- // Fix the bug which doesn't update the UI when _selfFrame is not set.
- // https://github.com/khawars/KSTokenView/issues/11
-
- if (tokens.count > 0) {
- updateLayout()
- }
- }
-
- // MARK: - Add Token
- /**
+ // _scrollView.setContentOffset(CGPoint(x: 0, y: 0), animated: false)
+ // _scrollView.contentSize = CGSize(width: width, height: height)
+ }
+
+ override open func draw(_ rect: CGRect) {
+ _selfFrame = rect
+ _setupCompleted = true
+ _updateText()
+
+ // Fix the bug which doesn't update the UI when _selfFrame is not set.
+ // https://github.com/khawars/KSTokenView/issues/11
+
+ if (tokens.count > 0) {
+ updateLayout()
+ }
+ }
+
+ // MARK: - Add Token
+ /**
Create and add new token
- parameter title: String value
- returns: KSToken object
*/
- func addTokenWithTitle(_ title: String) -> KSToken? {
- return addTokenWithTitle(title, tokenObject: nil)
- }
-
- /**
+ func addTokenWithTitle(_ title: String) -> KSToken? {
+ return addTokenWithTitle(title, tokenObject: nil)
+ }
+
+ /**
Create and add new token with custom object
- parameter title: String value
@@ -223,490 +229,490 @@ open class KSTokenField: UITextField {
- returns: KSToken object
*/
- func addTokenWithTitle(_ title: String, tokenObject: AnyObject?) -> KSToken? {
- let token = KSToken(title: title, object: tokenObject)
- return addToken(token)
- }
-
- /**
+ func addTokenWithTitle(_ title: String, tokenObject: AnyObject?) -> KSToken? {
+ let token = KSToken(title: title, object: tokenObject)
+ return addToken(token)
+ }
+
+ /**
Add new token
- parameter token: KSToken object
- returns: KSToken object
*/
- func addToken(_ token: KSToken) -> KSToken? {
- if (token.title.characters.count == 0) {
- token.title = "Untitled"
- }
-
- if (!tokens.contains(token)) {
- token.addTarget(self, action: #selector(KSTokenField.tokenTouchDown(_:)), for: .touchDown)
- token.addTarget(self, action: #selector(KSTokenField.tokenTouchUpInside(_:)), for: .touchUpInside)
- tokens.append(token)
- _insertToken(token)
- }
-
- return token
- }
-
- fileprivate func _insertToken(_ token: KSToken, shouldLayout: Bool = true) {
- _scrollView.addSubview(token)
- _scrollView.bringSubview(toFront: token)
- token.setNeedsDisplay()
- if shouldLayout == true {
- updateLayout()
- }
- }
-
- //MARK: - Delete Token
- /*
+ func addToken(_ token: KSToken) -> KSToken? {
+ if (token.title.characters.count == 0) {
+ token.title = "Untitled"
+ }
+
+ if (!tokens.contains(token)) {
+ token.addTarget(self, action: #selector(KSTokenField.tokenTouchDown(_:)), for: .touchDown)
+ token.addTarget(self, action: #selector(KSTokenField.tokenTouchUpInside(_:)), for: .touchUpInside)
+ tokens.append(token)
+ _insertToken(token)
+ }
+
+ return token
+ }
+
+ fileprivate func _insertToken(_ token: KSToken, shouldLayout: Bool = true) {
+ _scrollView.addSubview(token)
+ _scrollView.bringSubview(toFront: token)
+ token.setNeedsDisplay()
+ if shouldLayout == true {
+ updateLayout()
+ }
+ }
+
+ //MARK: - Delete Token
+ /*
**************************** Delete Token ****************************
*/
-
- /**
+
+ /**
Deletes a token from view
- parameter token: KSToken object
*/
- func deleteToken(_ token: KSToken) {
- removeToken(token)
- }
-
- /**
+ func deleteToken(_ token: KSToken) {
+ removeToken(token)
+ }
+
+ /**
Deletes a token from view, if any token is found for custom object
- parameter object: Custom object
*/
- func deleteTokenWithObject(_ object: AnyObject?) {
- if object == nil {return}
- for token in tokens {
- if (token.object!.isEqual(object)) {
- removeToken(token)
- break
- }
- }
- }
-
- /**
+ func deleteTokenWithObject(_ object: AnyObject?) {
+ if object == nil {return}
+ for token in tokens {
+ if (token.object!.isEqual(object)) {
+ removeToken(token)
+ break
+ }
+ }
+ }
+
+ /**
Deletes all tokens from view
*/
- func forceDeleteAllTokens() {
- tokens.removeAll(keepingCapacity: false)
- for token in tokens {
- removeToken(token, removingAll: true)
- }
- updateLayout()
- }
-
- /**
+ func forceDeleteAllTokens() {
+ tokens.removeAll(keepingCapacity: false)
+ for token in tokens {
+ removeToken(token, removingAll: true)
+ }
+ updateLayout()
+ }
+
+ /**
Deletes token from view
- parameter token: KSToken object
- parameter removingAll: A boolean to describe if removingAll tokens
*/
- func removeToken(_ token: KSToken, removingAll: Bool = false) {
- if token.isEqual(selectedToken) {
- deselectSelectedToken()
- }
- token.removeFromSuperview()
-
- let index = tokens.index(of: token)
- if (index != nil) {
- tokens.remove(at: index!)
- }
- if (!removingAll) {
- updateLayout()
- }
- }
-
-
- //MARK: - Layout
- /*
+ func removeToken(_ token: KSToken, removingAll: Bool = false) {
+ if token.isEqual(selectedToken) {
+ deselectSelectedToken()
+ }
+ token.removeFromSuperview()
+
+ let index = tokens.index(of: token)
+ if (index != nil) {
+ tokens.remove(at: index!)
+ }
+ if (!removingAll) {
+ updateLayout()
+ }
+ }
+
+
+ //MARK: - Layout
+ /*
**************************** Layout ****************************
*/
-
- /**
+
+ /**
Untokenzies the layout
*/
- func untokenize() {
- if (!_removesTokensOnEndEditing) {
- return
- }
- _state = .closed
- for subview in _scrollView.subviews {
- if subview is KSToken {
- subview.removeFromSuperview()
- }
- }
- updateLayout()
- }
-
- /**
+ func untokenize() {
+ if (!_removesTokensOnEndEditing) {
+ return
+ }
+ _state = .closed
+ for subview in _scrollView.subviews {
+ if subview is KSToken {
+ subview.removeFromSuperview()
+ }
+ }
+ updateLayout()
+ }
+
+ /**
Tokenizes the layout
*/
- func tokenize() {
- _state = .opened
- for token: KSToken in tokens {
- _insertToken(token, shouldLayout: false)
- }
- updateLayout()
- }
-
- /**
+ func tokenize() {
+ _state = .opened
+ for token: KSToken in tokens {
+ _insertToken(token, shouldLayout: false)
+ }
+ updateLayout()
+ }
+
+ /**
Updates the tokenView layout and calls delegate methods
*/
- func updateLayout(_ shouldUpdateText: Bool = true) {
- if (parentView == nil) {
- return
- }
-
- _caretPoint = _layoutTokens()
- deselectSelectedToken()
-
- if (shouldUpdateText) {
- _updateText()
- }
+ func updateLayout(_ shouldUpdateText: Bool = true) {
+ if (parentView == nil) {
+ return
+ }
+
+ _caretPoint = _layoutTokens()
+ deselectSelectedToken()
+
+ if (shouldUpdateText) {
+ _updateText()
+ }
+
+ if _caretPoint != .zero {
+ let tokensMaxY = max(_caretPoint!.y, _selfFrame!.height)
- if _caretPoint != .zero {
- let tokensMaxY = max(_caretPoint!.y, _selfFrame!.height)
-
- if (frame.size.height != tokensMaxY) {
- tokenFieldDelegate?.tokenFieldShouldChangeHeight(tokensMaxY)
- }
+ if (frame.size.height != tokensMaxY) {
+ tokenFieldDelegate?.tokenFieldShouldChangeHeight(tokensMaxY)
}
- }
-
- /**
+ }
+ }
+
+ /**
Layout tokens
- returns: CGPoint maximum position values
*/
- fileprivate func _layoutTokens() -> CGPoint {
- if (_selfFrame == nil) {
- return .zero
- }
-
- if (_state == .closed) {
- return CGPoint(x: _marginX! + _bufferX!, y: _selfFrame!.size.height)
- }
-
- if (_direction == .horizontal) {
- return _layoutTokensHorizontally()
- }
-
- var lineNumber = 1
- let leftMargin = _leftViewRect().width
- let rightMargin = _rightViewRect().width
- let tokenHeight = _font!.lineHeight + _paddingY!;
-
- var tokenPosition = CGPoint(x: _marginX!, y: _marginY!)
-
- for token: KSToken in tokens {
- let width = KSUtils.getRect(token.title as NSString, width: bounds.size.width, font: _font!).size.width + ceil(_paddingX!*2+1)
- let tokenWidth = min(width, token.maxWidth)
-
- // Add token at specific position
- if ((token.superview) != nil) {
- if (tokenPosition.x + tokenWidth + _marginX! + leftMargin > bounds.size.width - rightMargin) {
- lineNumber += 1
- tokenPosition.x = _marginX!
- tokenPosition.y += (tokenHeight + _marginY!);
- }
-
- token.frame = CGRect(x: tokenPosition.x, y: tokenPosition.y, width: tokenWidth, height: tokenHeight)
- tokenPosition.x += tokenWidth + _marginX!;
- }
- }
-
- // check if next token can be added in same line or new line
- if ((bounds.size.width) - (tokenPosition.x + _marginX!) - leftMargin < _minWidthForInput) {
- lineNumber += 1
- tokenPosition.x = _marginX!
- tokenPosition.y += (tokenHeight + _marginY!);
- }
-
- var positionY = (lineNumber == 1 && tokens.count == 0) ? _selfFrame!.size.height: (tokenPosition.y + tokenHeight + _marginY!)
- _scrollView.contentSize = CGSize(width: _scrollView.frame.width, height: positionY)
- if (positionY > maximumHeight) {
- positionY = maximumHeight
- }
-
- _scrollView.frame.size = CGSize(width: _scrollView.frame.width, height: positionY)
- scrollViewScrollToEnd()
+ fileprivate func _layoutTokens() -> CGPoint {
+ if (_selfFrame == nil) {
+ return .zero
+ }
+
+ if (_state == .closed) {
+ return CGPoint(x: _marginX! + _bufferX!, y: _selfFrame!.size.height)
+ }
+
+ if (_direction == .horizontal) {
+ return _layoutTokensHorizontally()
+ }
+
+ var lineNumber = 1
+ let leftMargin = _leftViewRect().width
+ let rightMargin = _rightViewRect().width
+ let tokenHeight = _font!.lineHeight + _paddingY!;
+
+ var tokenPosition = CGPoint(x: _marginX!, y: _marginY!)
+
+ for token: KSToken in tokens {
+ let width = KSUtils.getRect(token.title as NSString, width: bounds.size.width, font: _font!).size.width + ceil(_paddingX!*2+1)
+ let tokenWidth = min(width, token.maxWidth)
- return CGPoint(x: tokenPosition.x + leftMargin, y: positionY)
- }
-
-
- /**
+ // Add token at specific position
+ if ((token.superview) != nil) {
+ if (tokenPosition.x + tokenWidth + _marginX! + leftMargin > bounds.size.width - rightMargin) {
+ lineNumber += 1
+ tokenPosition.x = _marginX!
+ tokenPosition.y += (tokenHeight + _marginY!);
+ }
+
+ token.frame = CGRect(x: tokenPosition.x, y: tokenPosition.y, width: tokenWidth, height: tokenHeight)
+ tokenPosition.x += tokenWidth + _marginX!;
+ }
+ }
+
+ // check if next token can be added in same line or new line
+ if ((bounds.size.width) - (tokenPosition.x + _marginX!) - leftMargin < _minWidthForInput) {
+ lineNumber += 1
+ tokenPosition.x = _marginX!
+ tokenPosition.y += (tokenHeight + _marginY!);
+ }
+
+ var positionY = (lineNumber == 1 && tokens.count == 0) ? _selfFrame!.size.height: (tokenPosition.y + tokenHeight + _marginY!)
+ _scrollView.contentSize = CGSize(width: _scrollView.frame.width, height: positionY)
+ if (positionY > maximumHeight) {
+ positionY = maximumHeight
+ }
+
+ _scrollView.frame.size = CGSize(width: _scrollView.frame.width, height: positionY)
+ scrollViewScrollToEnd()
+
+ return CGPoint(x: tokenPosition.x + leftMargin, y: positionY)
+ }
+
+
+ /**
Layout tokens horizontally
- returns: CGPoint maximum position values
*/
- fileprivate func _layoutTokensHorizontally() -> CGPoint {
- let leftMargin = _leftViewRect().width
- let tokenHeight = _font!.lineHeight + _paddingY!;
-
- var tokenPosition = CGPoint(x: _marginX!, y: _marginY!)
+ fileprivate func _layoutTokensHorizontally() -> CGPoint {
+ let leftMargin = _leftViewRect().width
+ let tokenHeight = _font!.lineHeight + _paddingY!;
+
+ var tokenPosition = CGPoint(x: _marginX!, y: _marginY!)
+
+ for token: KSToken in tokens {
+ let width = KSUtils.getRect(token.title as NSString, width: bounds.size.width, font: _font!).size.width + ceil(_paddingX!*2+1)
+ let tokenWidth = min(width, token.maxWidth)
- for token: KSToken in tokens {
- let width = KSUtils.getRect(token.title as NSString, width: bounds.size.width, font: _font!).size.width + ceil(_paddingX!*2+1)
- let tokenWidth = min(width, token.maxWidth)
-
- if ((token.superview) != nil) {
- token.frame = CGRect(x: tokenPosition.x, y: tokenPosition.y, width: tokenWidth, height: tokenHeight)
- tokenPosition.x += tokenWidth + _marginX!;
- }
+ if ((token.superview) != nil) {
+ token.frame = CGRect(x: tokenPosition.x, y: tokenPosition.y, width: tokenWidth, height: tokenHeight)
+ tokenPosition.x += tokenWidth + _marginX!;
}
-
- let offsetWidth = ((tokenPosition.x + _marginX! + _leftViewRect().width) > (frame.width - _minWidthForInput)) ? _minWidthForInput : 0
- _scrollView.contentSize = CGSize(width: max(_scrollView.frame.width, tokenPosition.x + offsetWidth), height: frame.height)
- scrollViewScrollToEnd()
-
- return CGPoint(x: min(tokenPosition.x + leftMargin, frame.width - _minWidthForInput), y: frame.height)
- }
-
- /**
+ }
+
+ let offsetWidth = ((tokenPosition.x + _marginX! + _leftViewRect().width) > (frame.width - _minWidthForInput)) ? _minWidthForInput : 0
+ _scrollView.contentSize = CGSize(width: max(_scrollView.frame.width, tokenPosition.x + offsetWidth), height: frame.height)
+ scrollViewScrollToEnd()
+
+ return CGPoint(x: min(tokenPosition.x + leftMargin, frame.width - _minWidthForInput), y: frame.height)
+ }
+
+ /**
Scroll the tokens to end
*/
- func scrollViewScrollToEnd() {
- var bottomOffset: CGPoint
- switch _direction {
- case .vertical:
- bottomOffset = CGPoint(x: 0, y: _scrollView.contentSize.height - _scrollView.bounds.height)
- case .horizontal:
- bottomOffset = CGPoint(x: _scrollView.contentSize.width - _scrollView.bounds.width, y: 0)
- }
- _scrollView.setContentOffset(bottomOffset, animated: true)
- }
-
- //MARK: - Text Rect
- /*
+ func scrollViewScrollToEnd() {
+ var bottomOffset: CGPoint
+ switch _direction {
+ case .vertical:
+ bottomOffset = CGPoint(x: 0, y: _scrollView.contentSize.height - _scrollView.bounds.height)
+ case .horizontal:
+ bottomOffset = CGPoint(x: _scrollView.contentSize.width - _scrollView.bounds.width, y: 0)
+ }
+ _scrollView.setContentOffset(bottomOffset, animated: true)
+ }
+
+ //MARK: - Text Rect
+ /*
**************************** Text Rect ****************************
*/
-
- fileprivate func _textRectWithBounds(_ bounds: CGRect) -> CGRect {
- if (!_setupCompleted) {return .zero}
-
- if (tokens.count == 0 || _caretPoint == nil) {
- return CGRect(x: _leftViewRect().width + _marginX! + _bufferX!, y: _leftViewRect().origin.y, width: bounds.size.width-5, height: bounds.size.height)
- }
-
- if (tokens.count != 0 && _state == .closed) {
- return CGRect(x: _leftViewRect().maxX + _marginX! + _bufferX!, y: _leftViewRect().origin.y, width: (frame.size.width - _caretPoint!.x - _marginX!), height: bounds.size.height)
- }
+
+ fileprivate func _textRectWithBounds(_ bounds: CGRect) -> CGRect {
+ if (!_setupCompleted) {return .zero}
+
+ if (tokens.count == 0 || _caretPoint == nil) {
+ return CGRect(x: _leftViewRect().width + _marginX! + _bufferX!, y: _leftViewRect().origin.y, width: bounds.size.width-5, height: bounds.size.height)
+ }
+
+ if (tokens.count != 0 && _state == .closed) {
+ return CGRect(x: _leftViewRect().maxX + _marginX! + _bufferX!, y: _leftViewRect().origin.y, width: (frame.size.width - _caretPoint!.x - _marginX!), height: bounds.size.height)
+ }
+
+ return CGRect(x: _caretPoint!.x, y: floor((_caretPoint!.y - font!.lineHeight - (_marginY!))), width: (frame.size.width - _caretPoint!.x - _marginX!), height: bounds.size.height)
+ }
+
+ override open func leftViewRect(forBounds bounds: CGRect) -> CGRect {
+ return CGRect(x: _marginX!, y: (_selfFrame != nil) ? (_selfFrame!.height - _leftViewRect().height)*0.5: (bounds.height - _leftViewRect().height)*0.5, width: _leftViewRect().width, height: ceil(_leftViewRect().height))
+ }
+
+ override open func textRect(forBounds bounds: CGRect) -> CGRect {
+ return _textRectWithBounds(bounds)
+ }
+
+ override open func editingRect(forBounds bounds: CGRect) -> CGRect {
+ return _textRectWithBounds(bounds)
+ }
+
+ override open func placeholderRect(forBounds bounds: CGRect) -> CGRect {
+ return _textRectWithBounds(bounds)
+ }
+
+ fileprivate func _leftViewRect() -> CGRect {
+ if (leftViewMode == .never ||
+ (leftViewMode == .unlessEditing && isEditing) ||
+ (leftViewMode == .whileEditing && !isEditing)) {
+ return .zero
+ }
+ return leftView!.frame
+ }
+
+ fileprivate func _rightViewRect() -> CGRect {
+ if (rightViewMode == .never ||
+ rightViewMode == .unlessEditing && isEditing ||
+ rightViewMode == .whileEditing && !isEditing) {
+ return .zero
+ }
+ return rightView!.bounds
+ }
+
+
+ //MARK: - Prompt Text
+ /*
+ **************************** Prompt Text ****************************
+ */
+ fileprivate func _setPromptText(_ text: String?) {
+ if (text != nil) {
+ var label = leftView
+ if !(label is UILabel) {
+ label = UILabel(frame: .zero)
+ label?.frame.origin.x += _marginX!
+ leftViewMode = .always
+ }
+ (label as! UILabel).text = text
+ (label as! UILabel).font = font
+ (label as! UILabel).textColor = promptTextColor
+ (label as! UILabel).sizeToFit()
+ leftView = label
- return CGRect(x: _caretPoint!.x, y: floor((_caretPoint!.y - font!.lineHeight - (_marginY!))), width: (frame.size.width - _caretPoint!.x - _marginX!), height: bounds.size.height)
- }
-
- override open func leftViewRect(forBounds bounds: CGRect) -> CGRect {
- return CGRect(x: _marginX!, y: (_selfFrame != nil) ? (_selfFrame!.height - _leftViewRect().height)*0.5: (bounds.height - _leftViewRect().height)*0.5, width: _leftViewRect().width, height: ceil(_leftViewRect().height))
- }
-
- override open func textRect(forBounds bounds: CGRect) -> CGRect {
- return _textRectWithBounds(bounds)
- }
-
- override open func editingRect(forBounds bounds: CGRect) -> CGRect {
- return _textRectWithBounds(bounds)
- }
-
- override open func placeholderRect(forBounds bounds: CGRect) -> CGRect {
- return _textRectWithBounds(bounds)
- }
-
- fileprivate func _leftViewRect() -> CGRect {
- if (leftViewMode == .never ||
- (leftViewMode == .unlessEditing && isEditing) ||
- (leftViewMode == .whileEditing && !isEditing)) {
- return .zero
- }
- return leftView!.frame
- }
-
- fileprivate func _rightViewRect() -> CGRect {
- if (rightViewMode == .never ||
- rightViewMode == .unlessEditing && isEditing ||
- rightViewMode == .whileEditing && !isEditing) {
- return .zero
- }
- return rightView!.bounds
- }
-
-
- //MARK: - Prompt Text
- /*
- **************************** Prompt Text ****************************
- */
- fileprivate func _setPromptText(_ text: String?) {
- if (text != nil) {
- var label = leftView
- if !(label is UILabel) {
- label = UILabel(frame: .zero)
- label?.frame.origin.x += _marginX!
- leftViewMode = .always
- }
- (label as! UILabel).text = text
- (label as! UILabel).font = font
- (label as! UILabel).textColor = promptTextColor
- (label as! UILabel).sizeToFit()
- leftView = label
-
- } else {
- leftView = nil
- }
- _setScrollRect()
- }
-
-
- //MARK: - Placeholder
- /*
+ } else {
+ leftView = nil
+ }
+ _setScrollRect()
+ }
+
+
+ //MARK: - Placeholder
+ /*
**************************** Placeholder ****************************
*/
-
- fileprivate func _updateText() {
- if (!_setupCompleted) {return}
- _initPlaceholderLabel()
+
+ fileprivate func _updateText() {
+ if (!_setupCompleted) {return}
+ _initPlaceholderLabel()
+
+ switch(_state) {
+ case .opened:
+ text = KSTextEmpty
+ break
- switch(_state) {
- case .opened:
- text = KSTextEmpty
- break
-
- case .closed:
- if tokens.count == 0 {
- text = KSTextEmpty
-
- } else {
- var title = KSTextEmpty
- for token: KSToken in tokens {
- title += "\(token.title)\(_separatorText!)"
- }
-
- if (title.characters.count > 0) {
- title = title.substring(with: title.characters.index(title.startIndex, offsetBy: 0)..
bounds.width {
- text = "\(tokens.count) \(_descriptionText)"
- } else {
- text = title
- }
- }
- break
- }
- _updatePlaceHolderVisibility()
- }
-
- fileprivate func _updatePlaceHolderVisibility() {
- if tokens.count == 0 && (text == KSTextEmpty || text!.isEmpty) {
- _placeholderLabel?.text = _placeholderValue!
- _placeholderLabel?.sizeToFit()
- _placeholderLabel?.isHidden = false
-
- } else {
- _placeholderLabel?.isHidden = true
- }
- }
-
- fileprivate func _initPlaceholderLabel() {
- let xPos = _marginX!
- if (_placeholderLabel == nil) {
- _placeholderLabel = UILabel(frame: CGRect(x: xPos, y: leftView!.frame.origin.y, width: _selfFrame!.width - xPos - _leftViewRect().size.width, height: _leftViewRect().size.height))
- _placeholderLabel?.textColor = placeHolderColor
- _placeholderLabel?.font = _font
- _scrollView.addSubview(_placeholderLabel!)
+ case .closed:
+ if tokens.count == 0 {
+ text = KSTextEmpty
+
} else {
- _placeholderLabel?.frame.origin.x = xPos
- }
- }
-
-
- //MARK: - Token Gestures
- //__________________________________________________________________________________
- //
- func isSelectedToken(_ token: KSToken) -> Bool {
- if token.isEqual(selectedToken) {
- return true
- }
- return false
- }
-
-
- func deselectSelectedToken() {
- selectedToken?.isSelected = false
- selectedToken = nil
- }
-
- func selectToken(_ token: KSToken) {
- if (token.sticky) {
- return
- }
- for token: KSToken in tokens {
- if isSelectedToken(token) {
- deselectSelectedToken()
- break
- }
- }
+ var title = KSTextEmpty
+ for token: KSToken in tokens {
+ title += "\(token.title)\(_separatorText!)"
+ }
+
+ if (title.characters.count > 0) {
+ title = title.substring(with: title.characters.index(title.startIndex, offsetBy: 0).. bounds.width {
+ text = "\(tokens.count) \(_descriptionText)"
+ } else {
+ text = title
+ }
+ }
+ break
+ }
+ _updatePlaceHolderVisibility()
+ }
+
+ fileprivate func _updatePlaceHolderVisibility() {
+ if tokens.count == 0 && (text == KSTextEmpty || text!.isEmpty) {
+ _placeholderLabel?.text = _placeholderValue!
+ _placeholderLabel?.sizeToFit()
+ _placeholderLabel?.isHidden = false
- token.isSelected = true
- selectedToken = token
- tokenFieldDelegate?.tokenFieldDidSelectToken?(token)
- }
-
- func tokenTouchDown(_ token: KSToken) {
- if (selectedToken != nil) {
- selectedToken?.isSelected = false
- selectedToken = nil
- }
- }
-
- func tokenTouchUpInside(_ token: KSToken) {
- selectToken(token)
- }
-
- override open func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
- if (touch.view == self) {
- deselectSelectedToken()
- }
- return super.beginTracking(touch, with: event)
- }
-
- func tokenFieldTextDidChange(_ textField: UITextField) {
- _updatePlaceHolderVisibility()
- }
-
- // MARK: - Other Methods
-
- func paddingX() -> CGFloat? {
- return _paddingX
- }
-
- func tokenFont() -> UIFont? {
- return _font
- }
-
- func objects() -> NSArray {
- let objects = NSMutableArray()
- for object: AnyObject in tokens {
- objects.add(object)
- }
- return objects
- }
-
- override open func becomeFirstResponder() -> Bool {
- super.becomeFirstResponder()
- tokenFieldDelegate?.tokenFieldDidBeginEditing?(self)
+ } else {
+ _placeholderLabel?.isHidden = true
+ }
+ }
+
+ fileprivate func _initPlaceholderLabel() {
+ let xPos = _marginX!
+ if (_placeholderLabel == nil) {
+ _placeholderLabel = UILabel(frame: CGRect(x: xPos, y: leftView!.frame.origin.y, width: _selfFrame!.width - xPos - _leftViewRect().size.width, height: _leftViewRect().size.height))
+ _placeholderLabel?.textColor = placeHolderColor
+ _placeholderLabel?.font = _font
+ _scrollView.addSubview(_placeholderLabel!)
+ } else {
+ _placeholderLabel?.frame.origin.x = xPos
+ }
+ }
+
+
+ //MARK: - Token Gestures
+ //__________________________________________________________________________________
+ //
+ func isSelectedToken(_ token: KSToken) -> Bool {
+ if token.isEqual(selectedToken) {
return true
- }
-
- @discardableResult override open func resignFirstResponder() -> Bool {
- tokenFieldDelegate?.tokenFieldDidEndEditing?(self)
- return super.resignFirstResponder()
- }
-
+ }
+ return false
+ }
+
+
+ func deselectSelectedToken() {
+ selectedToken?.isSelected = false
+ selectedToken = nil
+ }
+
+ func selectToken(_ token: KSToken) {
+ if (token.sticky) {
+ return
+ }
+ for token: KSToken in tokens {
+ if isSelectedToken(token) {
+ deselectSelectedToken()
+ break
+ }
+ }
+
+ token.isSelected = true
+ selectedToken = token
+ tokenFieldDelegate?.tokenFieldDidSelectToken?(token)
+ }
+
+ func tokenTouchDown(_ token: KSToken) {
+ if (selectedToken != nil) {
+ selectedToken?.isSelected = false
+ selectedToken = nil
+ }
+ }
+
+ func tokenTouchUpInside(_ token: KSToken) {
+ selectToken(token)
+ }
+
+ override open func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
+ if (touch.view == self) {
+ deselectSelectedToken()
+ }
+ return super.beginTracking(touch, with: event)
+ }
+
+ func tokenFieldTextDidChange(_ textField: UITextField) {
+ _updatePlaceHolderVisibility()
+ }
+
+ // MARK: - Other Methods
+
+ func paddingX() -> CGFloat? {
+ return _paddingX
+ }
+
+ func tokenFont() -> UIFont? {
+ return _font
+ }
+
+ func objects() -> NSArray {
+ let objects = NSMutableArray()
+ for object: AnyObject in tokens {
+ objects.add(object)
+ }
+ return objects
+ }
+
+ override open func becomeFirstResponder() -> Bool {
+ super.becomeFirstResponder()
+ tokenFieldDelegate?.tokenFieldDidBeginEditing?(self)
+ return true
+ }
+
+ @discardableResult override open func resignFirstResponder() -> Bool {
+ tokenFieldDelegate?.tokenFieldDidEndEditing?(self)
+ return super.resignFirstResponder()
+ }
+
}
@@ -714,51 +720,51 @@ open class KSTokenField: UITextField {
//__________________________________________________________________________________
//
extension KSTokenField : UIScrollViewDelegate {
- public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
- _scrollPoint = scrollView.contentOffset
- }
-
- public func scrollViewDidScroll(_ aScrollView: UIScrollView) {
+ public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
+ _scrollPoint = scrollView.contentOffset
+ }
+
+ public func scrollViewDidScroll(_ aScrollView: UIScrollView) {
if (_state == .opened) {
- text = KSTextEmpty
- }
- updateCaretVisiblity(aScrollView)
- }
-
- func updateCaretVisiblity(_ aScrollView: UIScrollView) {
- switch _direction {
- case .vertical:
- let scrollViewHeight = aScrollView.frame.size.height;
- let scrollContentSizeHeight = aScrollView.contentSize.height;
- let scrollOffset = aScrollView.contentOffset.y;
-
- if (scrollOffset + scrollViewHeight < scrollContentSizeHeight - 10) {
- hideCaret()
-
- } else if (scrollOffset + scrollViewHeight >= scrollContentSizeHeight - 10) {
- showCaret()
- }
-
- case .horizontal:
- let scrollViewWidth = aScrollView.frame.size.width;
- let scrollContentSizeWidth = aScrollView.contentSize.width;
- let scrollOffset = aScrollView.contentOffset.x;
-
- if (scrollOffset + scrollViewWidth < scrollContentSizeWidth - 10) {
- hideCaret()
-
- } else if (scrollOffset + scrollViewWidth >= scrollContentSizeWidth - 10) {
- showCaret()
- }
-
+ text = KSTextEmpty
+ }
+ updateCaretVisiblity(aScrollView)
+ }
+
+ func updateCaretVisiblity(_ aScrollView: UIScrollView) {
+ switch _direction {
+ case .vertical:
+ let scrollViewHeight = aScrollView.frame.size.height;
+ let scrollContentSizeHeight = aScrollView.contentSize.height;
+ let scrollOffset = aScrollView.contentOffset.y;
+
+ if (scrollOffset + scrollViewHeight < scrollContentSizeHeight - 10) {
+ hideCaret()
+
+ } else if (scrollOffset + scrollViewHeight >= scrollContentSizeHeight - 10) {
+ showCaret()
}
- }
-
- func hideCaret() {
- tintColor = UIColor.clear
- }
-
- func showCaret() {
- tintColor = _cursorColor
- }
+
+ case .horizontal:
+ let scrollViewWidth = aScrollView.frame.size.width;
+ let scrollContentSizeWidth = aScrollView.contentSize.width;
+ let scrollOffset = aScrollView.contentOffset.x;
+
+ if (scrollOffset + scrollViewWidth < scrollContentSizeWidth - 10) {
+ hideCaret()
+
+ } else if (scrollOffset + scrollViewWidth >= scrollContentSizeWidth - 10) {
+ showCaret()
+ }
+
+ }
+ }
+
+ func hideCaret() {
+ tintColor = UIColor.clear
+ }
+
+ func showCaret() {
+ tintColor = _cursorColor
+ }
}
diff --git a/KSTokenView/KSTokenView.swift b/KSTokenView/KSTokenView.swift
old mode 100644
new mode 100755
index 4ab3527..8b4bc80
--- a/KSTokenView/KSTokenView.swift
+++ b/KSTokenView/KSTokenView.swift
@@ -26,13 +26,13 @@ import UIKit
@objc public enum KSTokenViewStyle: Int {
- case rounded
- case squared
+ case rounded
+ case squared
}
@objc public enum KSTokenViewScrollDirection: Int {
- case vertical
- case horizontal
+ case vertical
+ case horizontal
}
@@ -41,8 +41,8 @@ import UIKit
//
@objc public protocol KSTokenViewDelegate {
-
- /**
+
+ /**
Asks the delegate whether the token should be added
- parameter tokenView: KSTokenView object
@@ -51,36 +51,36 @@ import UIKit
- returns: Boolean
*/
- @objc optional func tokenView(_ tokenView: KSTokenView, shouldAddToken token: KSToken) -> Bool
- @objc optional func tokenView(_ tokenView: KSTokenView, willAddToken token: KSToken)
- @objc optional func tokenView(_ tokenView: KSTokenView, shouldChangeAppearanceForToken token: KSToken) -> KSToken?
- @objc optional func tokenView(_ tokenView: KSTokenView, didAddToken token: KSToken)
- @objc optional func tokenView(_ tokenView: KSTokenView, didFailToAdd token: KSToken)
-
- @objc optional func tokenView(_ tokenView: KSTokenView, shouldDeleteToken token: KSToken) -> Bool
- @objc optional func tokenView(_ tokenView: KSTokenView, willDeleteToken token: KSToken)
- @objc optional func tokenView(_ tokenView: KSTokenView, didDeleteToken token: KSToken)
- @objc optional func tokenView(_ tokenView: KSTokenView, didFailToDeleteToken token: KSToken)
-
- @objc optional func tokenView(_ tokenView: KSTokenView, willChangeFrameWithX: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat)
- @objc optional func tokenView(_ tokenView: KSTokenView, didChangeFrameWithX: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat)
-
- @objc optional func tokenView(_ tokenView: KSTokenView, didSelectToken token: KSToken)
- @objc optional func tokenViewDidBeginEditing(_ tokenView: KSTokenView)
- @objc optional func tokenViewDidEndEditing(_ tokenView: KSTokenView)
-
- func tokenView(_ token: KSTokenView, performSearchWithString string: String, completion: ((_ results: Array) -> Void)?)
- func tokenView(_ token: KSTokenView, displayTitleForObject object: AnyObject) -> String
- @objc optional func tokenView(_ token: KSTokenView, withObject object: AnyObject, tableView: UITableView, cellForRowAtIndexPath indexPath: IndexPath) -> UITableViewCell
- @objc optional func tokenView(_ token: KSTokenView, didSelectRowAtIndexPath indexPath: IndexPath)
-
- @objc optional func tokenViewShouldDeleteAllToken(_ tokenView: KSTokenView) -> Bool
- @objc optional func tokenViewWillDeleteAllToken(_ tokenView: KSTokenView)
- @objc optional func tokenViewDidDeleteAllToken(_ tokenView: KSTokenView)
- @objc optional func tokenViewDidFailToDeleteAllTokens(_ tokenView: KSTokenView)
-
- @objc optional func tokenViewDidShowSearchResults(_ tokenView: KSTokenView)
- @objc optional func tokenViewDidHideSearchResults(_ tokenView: KSTokenView)
+ @objc optional func tokenView(_ tokenView: KSTokenView, shouldAddToken token: KSToken) -> Bool
+ @objc optional func tokenView(_ tokenView: KSTokenView, willAddToken token: KSToken)
+ @objc optional func tokenView(_ tokenView: KSTokenView, shouldChangeAppearanceForToken token: KSToken) -> KSToken?
+ @objc optional func tokenView(_ tokenView: KSTokenView, didAddToken token: KSToken)
+ @objc optional func tokenView(_ tokenView: KSTokenView, didFailToAdd token: KSToken)
+
+ @objc optional func tokenView(_ tokenView: KSTokenView, shouldDeleteToken token: KSToken) -> Bool
+ @objc optional func tokenView(_ tokenView: KSTokenView, willDeleteToken token: KSToken)
+ @objc optional func tokenView(_ tokenView: KSTokenView, didDeleteToken token: KSToken)
+ @objc optional func tokenView(_ tokenView: KSTokenView, didFailToDeleteToken token: KSToken)
+
+ @objc optional func tokenView(_ tokenView: KSTokenView, willChangeFrameWithX: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat)
+ @objc optional func tokenView(_ tokenView: KSTokenView, didChangeFrameWithX: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat)
+
+ @objc optional func tokenView(_ tokenView: KSTokenView, didSelectToken token: KSToken)
+ @objc optional func tokenViewDidBeginEditing(_ tokenView: KSTokenView)
+ @objc optional func tokenViewDidEndEditing(_ tokenView: KSTokenView)
+
+ func tokenView(_ token: KSTokenView, performSearchWithString string: String, completion: ((_ results: Array) -> Void)?)
+ func tokenView(_ token: KSTokenView, displayTitleForObject object: AnyObject) -> String
+ @objc optional func tokenView(_ token: KSTokenView, withObject object: AnyObject, tableView: UITableView, cellForRowAtIndexPath indexPath: IndexPath) -> UITableViewCell
+ @objc optional func tokenView(_ token: KSTokenView, didSelectRowAtIndexPath indexPath: IndexPath)
+
+ @objc optional func tokenViewShouldDeleteAllToken(_ tokenView: KSTokenView) -> Bool
+ @objc optional func tokenViewWillDeleteAllToken(_ tokenView: KSTokenView)
+ @objc optional func tokenViewDidDeleteAllToken(_ tokenView: KSTokenView)
+ @objc optional func tokenViewDidFailToDeleteAllTokens(_ tokenView: KSTokenView)
+
+ @objc optional func tokenViewDidShowSearchResults(_ tokenView: KSTokenView)
+ @objc optional func tokenViewDidHideSearchResults(_ tokenView: KSTokenView)
}
//MARK: - KSTokenView
@@ -88,429 +88,435 @@ import UIKit
//
/**
-* A KSTokenView is a control that displays a collection of tokens in a an editable UITextField and sends messages to delegate object. It can be used to gather small amounts of text from user and perform search operation. User can choose multiple search results, which are displayed as token in UITextField.
-*/
+ * A KSTokenView is a control that displays a collection of tokens in a an editable UITextField and sends messages to delegate object. It can be used to gather small amounts of text from user and perform search operation. User can choose multiple search results, which are displayed as token in UITextField.
+ */
open class KSTokenView: UIView {
-
- //MARK: - Private Properties
- //__________________________________________________________________________________
- //
- fileprivate var _tokenField: KSTokenField!
- fileprivate var _searchTableView: UITableView = UITableView(frame: .zero, style: UITableViewStyle.plain)
- fileprivate var _resultArray = [AnyObject]()
- fileprivate var _showingSearchResult = false
- fileprivate var _indicator = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.gray)
- fileprivate let _searchResultHeight: CGFloat = 200.0
- fileprivate var _lastSearchString: String = ""
- fileprivate var _intrinsicContentHeight: CGFloat = UIViewNoIntrinsicMetric
-
- //MARK: - Public Properties
- //__________________________________________________________________________________
- //
-
- /// returns the value of field
- open var text : String {
- get {
- return _tokenField.text!.substring(with: _tokenField.text!.characters.index(_tokenField.text!.startIndex, offsetBy: 1).. KSToken? {
- if _tokenField.tokens.count == 0 {
- return nil
- }
- return _tokenField.tokens.last
- }
-
- fileprivate func _removeToken(_ token: KSToken, removingAll: Bool = false) {
- if token.sticky {return}
- if (!removingAll) {
- var shouldRemoveToken: Bool? = true
-
- if let shouldRemove = delegate?.tokenView?(self, shouldDeleteToken: token) {
- shouldRemoveToken = shouldRemove
- }
- if (shouldRemoveToken != true) {
- delegate?.tokenView?(self, didFailToDeleteToken: token)
- return
- }
- delegate?.tokenView?(self, willDeleteToken: token)
- }
- _tokenField.removeToken(token, removingAll: removingAll)
- if (!removingAll) {
- delegate?.tokenView?(self, didDeleteToken: token)
- _startSearchWithString("")
+ }
+
+ fileprivate func _lastToken() -> KSToken? {
+ if _tokenField.tokens.count == 0 {
+ return nil
+ }
+ return _tokenField.tokens.last
+ }
+
+ fileprivate func _removeToken(_ token: KSToken, removingAll: Bool = false) {
+ if token.sticky {return}
+ if (!removingAll) {
+ var shouldRemoveToken: Bool? = true
+
+ if let shouldRemove = delegate?.tokenView?(self, shouldDeleteToken: token) {
+ shouldRemoveToken = shouldRemove
}
- }
-
- fileprivate func _canAddMoreToken() -> Bool {
- if (maxTokenLimit != -1 && _tokenField.tokens.count >= maxTokenLimit) {
- _hideSearchResults()
- return false
+ if (shouldRemoveToken != true) {
+ delegate?.tokenView?(self, didFailToDeleteToken: token)
+ return
}
- return true
- }
-
-
- /**
+ delegate?.tokenView?(self, willDeleteToken: token)
+ }
+ _tokenField.removeToken(token, removingAll: removingAll)
+ if (!removingAll) {
+ delegate?.tokenView?(self, didDeleteToken: token)
+ _startSearchWithString("")
+ }
+ }
+
+ fileprivate func _canAddMoreToken() -> Bool {
+ if (maxTokenLimit != -1 && _tokenField.tokens.count >= maxTokenLimit) {
+ _hideSearchResults()
+ return false
+ }
+ return true
+ }
+
+
+ /**
Returns an Array of KSToken objects
- returns: Array of KSToken objects
*/
- open func tokens () -> Array? {
- return _tokenField.tokens
- }
-
- //MARK: - Add Token
- //__________________________________________________________________________________
- //
-
-
- /**
+ open func tokens () -> Array? {
+ return _tokenField.tokens
+ }
+
+ //MARK: - Add Token
+ //__________________________________________________________________________________
+ //
+
+
+ /**
Creates KSToken from input text, when user press keyboard "Done" button
- parameter tokenField: Field to add in
- returns: Boolean if token is added
*/
- fileprivate func _addTokenFromUntokenizedText(_ tokenField: KSTokenField) -> Bool {
- if (shouldAddTokenFromTextInput && tokenField.text != nil && tokenField.text != KSTextEmpty) {
- let trimmedString = tokenField.text!.trimmingCharacters(in: CharacterSet.whitespaces)
- addTokenWithTitle(trimmedString)
- _hideSearchResults()
- return true
- }
- return false
- }
-
- /**
+ fileprivate func _addTokenFromUntokenizedText(_ tokenField: KSTokenField) -> Bool {
+ if (shouldAddTokenFromTextInput && tokenField.text != nil && tokenField.text != KSTextEmpty) {
+ let trimmedString = tokenField.text!.trimmingCharacters(in: CharacterSet.whitespaces)
+ addTokenWithTitle(trimmedString)
+ _hideSearchResults()
+ return true
+ }
+ return false
+ }
+
+ /**
Creates and add a new KSToken object
- parameter title: Title of token
@@ -518,317 +524,317 @@ open class KSTokenView: UIView {
- returns: KSToken object
*/
- @discardableResult open func addTokenWithTitle(_ title: String, tokenObject: AnyObject? = nil) -> KSToken? {
- let token = KSToken(title: title, object: tokenObject)
- return addToken(token)
- }
-
-
- /**
+ @discardableResult open func addTokenWithTitle(_ title: String, tokenObject: AnyObject? = nil) -> KSToken? {
+ let token = KSToken(title: title, object: tokenObject)
+ return addToken(token)
+ }
+
+
+ /**
Creates and add a new KSToken object
- parameter token: KSToken object
- returns: KSToken object
*/
- @discardableResult open func addToken(_ token: KSToken) -> KSToken? {
- if (!_canAddMoreToken()) {
- return nil
- }
-
- var shouldAddToken: Bool? = true
- if let shouldAdd = delegate?.tokenView?(self, shouldAddToken: token) {
- shouldAddToken = shouldAdd
- }
-
- if (shouldAddToken != true) {
- delegate?.tokenView?(self, didFailToAdd: token)
- return nil
- }
-
- delegate?.tokenView?(self, willAddToken: token)
- var addedToken: KSToken?
- if let updatedToken = delegate?.tokenView?(self, shouldChangeAppearanceForToken: token) {
- addedToken = _tokenField.addToken(updatedToken)
-
- } else {
- addedToken = _tokenField.addToken(token)
- }
+ @discardableResult open func addToken(_ token: KSToken) -> KSToken? {
+ if (!_canAddMoreToken()) {
+ return nil
+ }
+
+ var shouldAddToken: Bool? = true
+ if let shouldAdd = delegate?.tokenView?(self, shouldAddToken: token) {
+ shouldAddToken = shouldAdd
+ }
+
+ if (shouldAddToken != true) {
+ delegate?.tokenView?(self, didFailToAdd: token)
+ return nil
+ }
+
+ delegate?.tokenView?(self, willAddToken: token)
+ var addedToken: KSToken?
+ if let updatedToken = delegate?.tokenView?(self, shouldChangeAppearanceForToken: token) {
+ addedToken = _tokenField.addToken(updatedToken)
- delegate?.tokenView?(self, didAddToken: addedToken!)
- return addedToken
- }
-
-
- //MARK: - Delete Token
- //__________________________________________________________________________________
- //
-
- /**
+ } else {
+ addedToken = _tokenField.addToken(token)
+ }
+
+ delegate?.tokenView?(self, didAddToken: addedToken!)
+ return addedToken
+ }
+
+
+ //MARK: - Delete Token
+ //__________________________________________________________________________________
+ //
+
+ /**
Deletes an already added KSToken object
- parameter token: KSToken object
*/
- open func deleteToken(_ token: KSToken) {
- _removeToken(token)
- }
-
- /**
+ open func deleteToken(_ token: KSToken) {
+ _removeToken(token)
+ }
+
+ /**
Searches for KSToken object and deletes
- parameter object: Custom object
*/
- open func deleteTokenWithObject(_ object: AnyObject?) {
- if object == nil {return}
- for token in _tokenField.tokens {
- if (token.object!.isEqual(object)) {
- _removeToken(token)
- break
- }
+ open func deleteTokenWithObject(_ object: AnyObject?) {
+ if object == nil {return}
+ for token in _tokenField.tokens {
+ if (token.object!.isEqual(object)) {
+ _removeToken(token)
+ break
}
- }
-
- /**
+ }
+ }
+
+ /**
Deletes all added tokens. This doesn't delete sticky token
*/
- open func deleteAllTokens() {
- if (_tokenField.tokens.count == 0) {return}
- var shouldDeleteAllTokens: Bool? = true
-
- if let shouldRemoveAll = delegate?.tokenViewShouldDeleteAllToken?(self) {
- shouldDeleteAllTokens = shouldRemoveAll
- }
-
- if (shouldDeleteAllTokens != true) {
- delegate?.tokenViewDidFailToDeleteAllTokens?(self)
- return
- }
-
- delegate?.tokenViewWillDeleteAllToken?(self)
- for token in _tokenField.tokens {_removeToken(token, removingAll: true)}
- _tokenField.updateLayout()
- delegate?.tokenViewDidDeleteAllToken?(self)
-
- if (_showingSearchResult) {
- _startSearchWithString(_lastSearchString)
- }
- }
-
- /**
+ open func deleteAllTokens() {
+ if (_tokenField.tokens.count == 0) {return}
+ var shouldDeleteAllTokens: Bool? = true
+
+ if let shouldRemoveAll = delegate?.tokenViewShouldDeleteAllToken?(self) {
+ shouldDeleteAllTokens = shouldRemoveAll
+ }
+
+ if (shouldDeleteAllTokens != true) {
+ delegate?.tokenViewDidFailToDeleteAllTokens?(self)
+ return
+ }
+
+ delegate?.tokenViewWillDeleteAllToken?(self)
+ for token in _tokenField.tokens {_removeToken(token, removingAll: true)}
+ _tokenField.updateLayout()
+ delegate?.tokenViewDidDeleteAllToken?(self)
+
+ if (_showingSearchResult) {
+ _startSearchWithString(_lastSearchString)
+ }
+ }
+
+ /**
Deletes last added KSToken object
*/
- open func deleteLastToken() {
- let token: KSToken? = _lastToken()
- if token != nil {
- _removeToken(token!)
- }
- }
-
- /**
+ open func deleteLastToken() {
+ let token: KSToken? = _lastToken()
+ if token != nil {
+ _removeToken(token!)
+ }
+ }
+
+ /**
Deletes selected KSToken object
*/
- open func deleteSelectedToken() {
- let token: KSToken? = selectedToken()
- if (token != nil) {
- _removeToken(token!)
- }
- }
-
- /**
+ open func deleteSelectedToken() {
+ let token: KSToken? = selectedToken()
+ if (token != nil) {
+ _removeToken(token!)
+ }
+ }
+
+ /**
Returns Selected KSToken object
- returns: KSToken object
*/
- open func selectedToken() -> KSToken? {
- return _tokenField.selectedToken
- }
-
-
- //MARK: - KSTokenFieldDelegates
- //__________________________________________________________________________________
- //
- func tokenFieldDidBeginEditing(_ tokenField: KSTokenField) {
- delegate?.tokenViewDidBeginEditing?(self)
- tokenField.tokenize()
- if (minimumCharactersToSearch == 0) {
- _startSearchWithString("")
- }
- }
-
- func tokenFieldDidEndEditing(_ tokenField: KSTokenField) {
- delegate?.tokenViewDidEndEditing?(self)
- tokenField.untokenize()
- _hideSearchResults()
- }
-
- open override var isFirstResponder : Bool {
- return _tokenField.isFirstResponder
- }
-
- override open func becomeFirstResponder() -> Bool {
- return _tokenField.becomeFirstResponder()
- }
-
- @discardableResult override open func resignFirstResponder() -> Bool {
- if (!_addTokenFromUntokenizedText(_tokenField)) {
- _tokenField.resignFirstResponder()
- }
- return false
- }
-
- //MARK: - Search
- //__________________________________________________________________________________
- //
-
- /**
+ open func selectedToken() -> KSToken? {
+ return _tokenField.selectedToken
+ }
+
+
+ //MARK: - KSTokenFieldDelegates
+ //__________________________________________________________________________________
+ //
+ func tokenFieldDidBeginEditing(_ tokenField: KSTokenField) {
+ delegate?.tokenViewDidBeginEditing?(self)
+ tokenField.tokenize()
+ if (minimumCharactersToSearch == 0) {
+ _startSearchWithString("")
+ }
+ }
+
+ func tokenFieldDidEndEditing(_ tokenField: KSTokenField) {
+ delegate?.tokenViewDidEndEditing?(self)
+ tokenField.untokenize()
+ _hideSearchResults()
+ }
+
+ open override var isFirstResponder : Bool {
+ return _tokenField.isFirstResponder
+ }
+
+ override open func becomeFirstResponder() -> Bool {
+ return _tokenField.becomeFirstResponder()
+ }
+
+ @discardableResult override open func resignFirstResponder() -> Bool {
+ if (!_addTokenFromUntokenizedText(_tokenField)) {
+ _tokenField.resignFirstResponder()
+ }
+ return false
+ }
+
+ //MARK: - Search
+ //__________________________________________________________________________________
+ //
+
+ /**
Triggers the search after user input text
- parameter string: Search keyword
*/
- fileprivate func _startSearchWithString(_ string: String) {
- if (!_canAddMoreToken()) {
- return
- }
- _showEmptyResults()
- _showActivityIndicator()
-
- let trimmedSearchString = string.trimmingCharacters(in: CharacterSet.whitespaces)
- delegate?.tokenView(self, performSearchWithString:trimmedSearchString, completion: { (results) -> Void in
- self._hideActivityIndicator()
- if (results.count > 0) {
- self._displayData(results)
- }
- })
- }
-
- fileprivate func _displayData(_ results: Array) {
- _resultArray = _filteredSearchResults(results)
- _searchTableView.reloadData()
- _showSearchResults()
- }
-
- fileprivate func _showEmptyResults() {
- _resultArray.removeAll(keepingCapacity: false)
- _searchTableView.reloadData()
- _showSearchResults()
- }
-
- fileprivate func _showSearchResults() {
- guard !_showingSearchResult else {return}
- _showingSearchResult = true
- addSubview(_searchTableView)
- let tokenFieldHeight = _tokenField.frame.height
- _searchTableView.isHidden = false
- _changeHeight(tokenFieldHeight)
- delegate?.tokenViewDidShowSearchResults?(self)
+ fileprivate func _startSearchWithString(_ string: String) {
+ if (!_canAddMoreToken()) {
+ return
}
-
- fileprivate func _hideSearchResults() {
- guard _showingSearchResult else {return}
- _showingSearchResult = false
- let searchTableView = self._searchTableView
- _changeHeight(_tokenField.frame.height) {
- searchTableView.isHidden = true
- searchTableView.removeFromSuperview()
- }
- delegate?.tokenViewDidHideSearchResults?(self)
+ _showEmptyResults()
+ _showActivityIndicator()
+
+ let trimmedSearchString = string.trimmingCharacters(in: CharacterSet.whitespaces)
+ delegate?.tokenView(self, performSearchWithString:trimmedSearchString, completion: { (results) -> Void in
+ self._hideActivityIndicator()
+ if (results.count > 0) {
+ self._displayData(results)
+ }
+ })
+ }
+
+ fileprivate func _displayData(_ results: Array) {
+ _resultArray = _filteredSearchResults(results)
+ _searchTableView.reloadData()
+ _showSearchResults()
+ }
+
+ fileprivate func _showEmptyResults() {
+ _resultArray.removeAll(keepingCapacity: false)
+ _searchTableView.reloadData()
+ _showSearchResults()
+ }
+
+ fileprivate func _showSearchResults() {
+ guard !_showingSearchResult else {return}
+ _showingSearchResult = true
+ addSubview(_searchTableView)
+ let tokenFieldHeight = _tokenField.frame.height
+ _searchTableView.isHidden = false
+ _changeHeight(tokenFieldHeight)
+ delegate?.tokenViewDidShowSearchResults?(self)
+ }
+
+ fileprivate func _hideSearchResults() {
+ guard _showingSearchResult else {return}
+ _showingSearchResult = false
+ let searchTableView = self._searchTableView
+ _changeHeight(_tokenField.frame.height) {
+ searchTableView.isHidden = true
+ searchTableView.removeFromSuperview()
}
-
- fileprivate func _repositionSearchResults(_ height: CGFloat) {
- if (!_showingSearchResult) {
- return
- }
- _searchTableView.frame.origin = CGPoint(x: 0, y: height)
- }
-
- fileprivate func _filteredSearchResults(_ results: Array ) -> Array {
- var filteredResults: Array = Array()
+ delegate?.tokenViewDidHideSearchResults?(self)
+ }
+
+ fileprivate func _repositionSearchResults(_ height: CGFloat) {
+ if (!_showingSearchResult) {
+ return
+ }
+ _searchTableView.frame.origin = CGPoint(x: 0, y: height)
+ }
+
+ fileprivate func _filteredSearchResults(_ results: Array ) -> Array {
+ var filteredResults: Array = Array()
+
+ for object: AnyObject in results {
+ // Check duplicates in array
+ var shouldAdd = !(filteredResults as NSArray).contains(object)
- for object: AnyObject in results {
- // Check duplicates in array
- var shouldAdd = !(filteredResults as NSArray).contains(object)
-
- if (shouldAdd) {
- if (!shouldDisplayAlreadyTokenized && _tokenField.tokens.count > 0) {
-
- // Search if already tokenized
- for token: KSToken in _tokenField.tokens {
- if (object.isEqual(token.object)) {
- shouldAdd = false
- break
- }
- }
- }
-
- if (shouldAdd) {
- filteredResults.append(object)
+ if (shouldAdd) {
+ if (!shouldDisplayAlreadyTokenized && _tokenField.tokens.count > 0) {
+
+ // Search if already tokenized
+ for token: KSToken in _tokenField.tokens {
+ if (object.isEqual(token.object)) {
+ shouldAdd = false
+ break
}
- }
- }
-
- if (shouldSortResultsAlphabatically) {
- return filteredResults.sorted(by: { s1, s2 in return self._sortStringForObject(s1) < self._sortStringForObject(s2) })
+ }
+ }
+
+ if (shouldAdd) {
+ filteredResults.append(object)
+ }
}
- return filteredResults
- }
-
- fileprivate func _sortStringForObject(_ object: AnyObject) -> String {
- let title = (delegate?.tokenView(self, displayTitleForObject: object))!
- return title
- }
-
- fileprivate func _showActivityIndicator() {
- _indicator.startAnimating()
- _searchTableView.tableHeaderView = _indicator
- }
-
- fileprivate func _hideActivityIndicator() {
- _indicator.stopAnimating()
- _searchTableView.tableHeaderView = nil
- }
+ }
- fileprivate func _changeHeight(_ tokenFieldHeight: CGFloat, completion: (() -> Void)? = nil) {
- let fullHeight = tokenFieldHeight + (_showingSearchResult ? _searchResultHeight : 0.0)
- delegate?.tokenView?(self, willChangeFrameWithX: frame.origin.x, y: frame.origin.y, width: frame.size.width, height: fullHeight)
- self._repositionSearchResults(tokenFieldHeight)
-
- UIView.animate(
- withDuration: animateDuration,
- animations: {
- self._tokenField.frame.size.height = tokenFieldHeight
- self.frame.size.height = fullHeight
- self._intrinsicContentHeight = fullHeight
- self.invalidateIntrinsicContentSize()
- self.superview?.layoutIfNeeded()
- },
- completion: {completed in
- completion?()
- if (completed) {
- self.delegate?.tokenView?(self, didChangeFrameWithX: self.frame.origin.x, y: self.frame.origin.y, width: self.frame.size.width, height: fullHeight)
- }
- })
+ if (shouldSortResultsAlphabatically) {
+ return filteredResults.sorted(by: { s1, s2 in return self._sortStringForObject(s1) < self._sortStringForObject(s2) })
}
-
- //MARK: - Memory Mangement
- //__________________________________________________________________________________
- //
- deinit {
-
- }
-
+ return filteredResults
+ }
+
+ fileprivate func _sortStringForObject(_ object: AnyObject) -> String {
+ let title = (delegate?.tokenView(self, displayTitleForObject: object))!
+ return title
+ }
+
+ fileprivate func _showActivityIndicator() {
+ _indicator.startAnimating()
+ _searchTableView.tableHeaderView = _indicator
+ }
+
+ fileprivate func _hideActivityIndicator() {
+ _indicator.stopAnimating()
+ _searchTableView.tableHeaderView = nil
+ }
+
+ fileprivate func _changeHeight(_ tokenFieldHeight: CGFloat, completion: (() -> Void)? = nil) {
+ let fullHeight = tokenFieldHeight + (_showingSearchResult ? _searchResultHeight : 0.0)
+ delegate?.tokenView?(self, willChangeFrameWithX: frame.origin.x, y: frame.origin.y, width: frame.size.width, height: fullHeight)
+ self._repositionSearchResults(tokenFieldHeight)
+
+ UIView.animate(
+ withDuration: animateDuration,
+ animations: {
+ self._tokenField.frame.size.height = tokenFieldHeight
+ self.frame.size.height = fullHeight
+ self._intrinsicContentHeight = fullHeight
+ self.invalidateIntrinsicContentSize()
+ self.superview?.layoutIfNeeded()
+ },
+ completion: {completed in
+ completion?()
+ if (completed) {
+ self.delegate?.tokenView?(self, didChangeFrameWithX: self.frame.origin.x, y: self.frame.origin.y, width: self.frame.size.width, height: fullHeight)
+ }
+ })
+ }
+
+ //MARK: - Memory Mangement
+ //__________________________________________________________________________________
+ //
+ deinit {
+
+ }
+
}
//MARK: - Extension KSTokenFieldDelegate
//__________________________________________________________________________________
//
extension KSTokenView : KSTokenFieldDelegate {
- func tokenFieldDidSelectToken(_ token: KSToken) {
- delegate?.tokenView?(self, didSelectToken: token)
- }
-
- func tokenFieldShouldChangeHeight(_ height: CGFloat) {
- _changeHeight(height)
- }
+ func tokenFieldDidSelectToken(_ token: KSToken) {
+ delegate?.tokenView?(self, didSelectToken: token)
+ }
+
+ func tokenFieldShouldChangeHeight(_ height: CGFloat) {
+ _changeHeight(height)
+ }
}
@@ -836,69 +842,69 @@ extension KSTokenView : KSTokenFieldDelegate {
//__________________________________________________________________________________
//
extension KSTokenView : UITextFieldDelegate {
- public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
+ public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
- // If backspace is pressed
- if (_tokenField.tokens.count > 0 && _tokenField.text == KSTextEmpty && string.isEmpty == true && shouldDeleteTokenOnBackspace) {
- if (_lastToken() != nil) {
- if (selectedToken() != nil) {
- deleteSelectedToken()
- } else {
- _tokenField.selectToken(_lastToken()!)
- }
- }
- return false
- }
-
- // Prevent removing KSTextEmpty
- if (string.isEmpty == true && _tokenField.text == KSTextEmpty) {
- return false
+ // If backspace is pressed
+ if (_tokenField.tokens.count > 0 && _tokenField.text == KSTextEmpty && string.isEmpty == true && shouldDeleteTokenOnBackspace) {
+ if (_lastToken() != nil) {
+ if (selectedToken() != nil) {
+ deleteSelectedToken()
+ } else {
+ _tokenField.selectToken(_lastToken()!)
+ }
}
+ return false
+ }
- var searchString: String
- let olderText = _tokenField.text
- var olderTextTrimmed = olderText!
- // remove the empty text marker from the beginning of the string
- if (olderText?.characters.first == KSTextEmpty.characters.first) {
- olderTextTrimmed = olderText!.substring(from: olderText!.characters.index(olderText!.startIndex, offsetBy: 1))
- }
+ // Prevent removing KSTextEmpty
+ if (string.isEmpty == true && _tokenField.text == KSTextEmpty) {
+ return false
+ }
- // Check if character is removed at some index
- // Remove character at that index
- if (string.isEmpty) {
- let first: String = olderText!.substring(to: olderText!.characters.index(olderText!.startIndex, offsetBy: range.location)) as String
- let second: String = olderText!.substring(from: olderText!.characters.index(olderText!.startIndex, offsetBy: range.location+1)) as String
- searchString = first + second
- searchString = searchString.trimmingCharacters(in: CharacterSet.whitespaces)
-
- } else { // new character added
- if (tokenizingCharacters.contains(string) && olderText != KSTextEmpty && olderTextTrimmed != "") {
- addTokenWithTitle(olderTextTrimmed, tokenObject: nil)
- _hideSearchResults()
- return false
- }
- searchString = (olderText! as NSString).replacingCharacters(in: range, with: string)
- if (searchString.characters.first == KSTextEmpty.characters.first) {
- searchString = searchString.substring(from: searchString.characters.index(searchString.startIndex, offsetBy: 1))
- }
- }
+ var searchString: String
+ let olderText = _tokenField.text
+ var olderTextTrimmed = olderText!
+ // remove the empty text marker from the beginning of the string
+ if (olderText?.characters.first == KSTextEmpty.characters.first) {
+ olderTextTrimmed = olderText!.substring(from: olderText!.characters.index(olderText!.startIndex, offsetBy: 1))
+ }
- // Allow all other characters
- if (searchString.characters.count >= minimumCharactersToSearch && searchString != "\n") {
- _lastSearchString = searchString
- _startSearchWithString(_lastSearchString)
- } else {
- _hideSearchResults()
+ // Check if character is removed at some index
+ // Remove character at that index
+ if (string.isEmpty) {
+ let first: String = olderText!.substring(to: olderText!.characters.index(olderText!.startIndex, offsetBy: range.location)) as String
+ let second: String = olderText!.substring(from: olderText!.characters.index(olderText!.startIndex, offsetBy: range.location+1)) as String
+ searchString = first + second
+ searchString = searchString.trimmingCharacters(in: CharacterSet.whitespaces)
+
+ } else { // new character added
+ if (tokenizingCharacters.contains(string) && olderText != KSTextEmpty && olderTextTrimmed != "") {
+ addTokenWithTitle(olderTextTrimmed, tokenObject: nil)
+ _hideSearchResults()
+ return false
+ }
+ searchString = (olderText! as NSString).replacingCharacters(in: range, with: string)
+ if (searchString.characters.first == KSTextEmpty.characters.first) {
+ searchString = searchString.substring(from: searchString.characters.index(searchString.startIndex, offsetBy: 1))
}
+ }
- _tokenField.scrollViewScrollToEnd()
- return true
- }
-
- public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
- resignFirstResponder()
- return true
- }
+ // Allow all other characters
+ if (searchString.characters.count >= minimumCharactersToSearch && searchString != "\n") {
+ _lastSearchString = searchString
+ _startSearchWithString(_lastSearchString)
+ } else {
+ _hideSearchResults()
+ }
+
+ _tokenField.scrollViewScrollToEnd()
+ return true
+ }
+
+ public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
+ resignFirstResponder()
+ return true
+ }
}
//MARK: - Extension UITableViewDelegate
@@ -906,49 +912,49 @@ extension KSTokenView : UITextFieldDelegate {
//
extension KSTokenView : UITableViewDelegate {
-
- public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
- delegate?.tokenView?(self, didSelectRowAtIndexPath: indexPath)
- let object: AnyObject = _resultArray[(indexPath as NSIndexPath).row]
- let title = delegate?.tokenView(self, displayTitleForObject: object)
- let token = KSToken(title: title!, object: object)
- addToken(token)
+
+ public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+ delegate?.tokenView?(self, didSelectRowAtIndexPath: indexPath)
+ let object: AnyObject = _resultArray[(indexPath as NSIndexPath).row]
+ let title = delegate?.tokenView(self, displayTitleForObject: object)
+ let token = KSToken(title: title!, object: object)
+ addToken(token)
+
+ if (shouldHideSearchResultsOnSelect) {
+ _hideSearchResults()
- if (shouldHideSearchResultsOnSelect) {
- _hideSearchResults()
-
- } else if (!shouldDisplayAlreadyTokenized) {
- _resultArray.remove(at: (indexPath as NSIndexPath).row)
- tableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.left)
- }
- }
+ } else if (!shouldDisplayAlreadyTokenized) {
+ _resultArray.remove(at: (indexPath as NSIndexPath).row)
+ tableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.left)
+ }
+ }
}
//MARK: - Extension UITableViewDataSource
//__________________________________________________________________________________
//
extension KSTokenView : UITableViewDataSource {
-
- public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
- return _resultArray.count
- }
-
- public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
-
- var cell: UITableViewCell? = delegate?.tokenView?(self, withObject: _resultArray[(indexPath as NSIndexPath).row], tableView: tableView, cellForRowAtIndexPath: indexPath)
- if cell != nil {
- return cell!
- }
-
- let cellIdentifier = "KSSearchTableCell"
- cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) as UITableViewCell?
- if (cell == nil) {
- cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: cellIdentifier)
- }
-
- let title = delegate?.tokenView(self, displayTitleForObject: _resultArray[(indexPath as NSIndexPath).row])
- cell!.textLabel!.text = (title != nil) ? title : "No Title"
- cell!.selectionStyle = UITableViewCellSelectionStyle.none
+
+ public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+ return _resultArray.count
+ }
+
+ public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+
+ var cell: UITableViewCell? = delegate?.tokenView?(self, withObject: _resultArray[(indexPath as NSIndexPath).row], tableView: tableView, cellForRowAtIndexPath: indexPath)
+ if cell != nil {
return cell!
- }
+ }
+
+ let cellIdentifier = "KSSearchTableCell"
+ cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) as UITableViewCell?
+ if (cell == nil) {
+ cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: cellIdentifier)
+ }
+
+ let title = delegate?.tokenView(self, displayTitleForObject: _resultArray[(indexPath as NSIndexPath).row])
+ cell!.textLabel!.text = (title != nil) ? title : "No Title"
+ cell!.selectionStyle = UITableViewCellSelectionStyle.none
+ return cell!
+ }
}