diff --git a/.jazzy.yml b/.jazzy.yml index 465c756..ab755d1 100644 --- a/.jazzy.yml +++ b/.jazzy.yml @@ -5,6 +5,6 @@ github_url: https://github.com/polydice/ICInputAccessory github_file_prefix: https://github.com/polydice/ICInputAccessory/blob/develop xcodebuild_arguments: [-project, ICInputAccessory.xcodeproj, -scheme, ICInputAccessory-iOS] module: ICInputAccessory -module_version: 1.3.0 +module_version: 1.4.0 output: docs/output theme: fullwidth diff --git a/.travis.yml b/.travis.yml index b1bfad3..e7c00b8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,6 +15,8 @@ script: - bundle exec rake ci:build - make -B carthage - make -B docs +after_script: + - sh scripts/update-docs.sh notifications: email: false slack: diff --git a/CHANGELOG.md b/CHANGELOG.md index 2505371..61c0c79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +## v1.4.0 + +* Added delegate methods: + + ```swift + @objc optional func tokenField(_ tokenField: ICTokenField, didChangeInputText text: String) + @objc optional func tokenField(_ tokenField: ICTokenField, shouldCompleteText text: String) -> Bool + @objc optional func tokenField(_ tokenField: ICTokenField, subsequentDelimiterForCompletedText text: String) -> String + ``` + +* Renamed delegate method: + + ```swift + @objc optional func tokenField(_ tokenField: ICTokenField, didCompleteText text: String) + ``` + ## v1.3.0 * Swift 3.0 diff --git a/Example/CustomizedTokenViewController.swift b/Example/CustomizedTokenViewController.swift index 907ef61..b167724 100644 --- a/Example/CustomizedTokenViewController.swift +++ b/Example/CustomizedTokenViewController.swift @@ -37,6 +37,8 @@ class CustomizedTokenViewController: UIViewController, ICTokenFieldDelegate { override func loadView() { super.loadView() view.backgroundColor = UIColor.white + textView.isEditable = false + textView.isSelectable = false textView.text = "[\n\n]" textView.font = UIFont.preferredFont(forTextStyle: .subheadline) textView.frame = view.bounds.insetBy(dx: 10, dy: 10) @@ -83,16 +85,29 @@ class CustomizedTokenViewController: UIViewController, ICTokenFieldDelegate { print(#function) } - func tokenField(_ tokenField: ICTokenField, didEnterText text: String) { - print("Add: \"\(text)\"") + func tokenField(_ tokenField: ICTokenField, didChangeInputText text: String) { + print("Typing \"\(text)\"") + } + + func tokenField(_ tokenField: ICTokenField, shouldCompleteText text: String) -> Bool { + print("Should add \"\(text)\"?") + return text != "42" + } + + func tokenField(_ tokenField: ICTokenField, didCompleteText text: String) { + print("Added \"\(text)\"") updateTexts() } func tokenField(_ tokenField: ICTokenField, didDeleteText text: String, atIndex index: Int) { - print("Delete: \"\(text)\"") + print("Deleted \"\(text)\"") updateTexts() } + func tokenField(_ tokenField: ICTokenField, subsequentDelimiterForCompletedText text: String) -> String { + return " ," + } + // MARK: - UIResponder Callbacks @objc fileprivate func dismiss(_ sender: UIBarButtonItem) { diff --git a/Example/Info.plist b/Example/Info.plist index eee55eb..4a13d88 100644 --- a/Example/Info.plist +++ b/Example/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.3.0 + 1.4.0 CFBundleSignature ???? CFBundleVersion diff --git a/ICInputAccessory.podspec b/ICInputAccessory.podspec index f5a705d..cfbbaf8 100644 --- a/ICInputAccessory.podspec +++ b/ICInputAccessory.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ICInputAccessory" - s.version = "1.3.0" + s.version = "1.4.0" s.summary = "A customized token text field used in the iCook app." s.description = <<-DESC ICKeyboardDismissTextField: diff --git a/ICInputAccessory/Info.plist b/ICInputAccessory/Info.plist index 9f4a4aa..8bce5c7 100644 --- a/ICInputAccessory/Info.plist +++ b/ICInputAccessory/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.3.0 + 1.4.0 CFBundleSignature ???? CFBundleVersion diff --git a/ICInputAccessoryUITests/Info.plist b/ICInputAccessoryUITests/Info.plist index 3fee538..c48e13f 100644 --- a/ICInputAccessoryUITests/Info.plist +++ b/ICInputAccessoryUITests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 1.3.0 + 1.4.0 CFBundleSignature ???? CFBundleVersion diff --git a/Makefile b/Makefile index 8b63836..bd5efb9 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,6 @@ install: brew-install bundle-install pod-install brew-install: - brew update brew tap homebrew/bundle brew bundle @@ -11,7 +10,10 @@ bundle-install: pod-install: bundle exec pod install --no-repo-update -setup: brew-install +bootstrap: + brew tap homebrew/bundle + brew bundle + gem install bundler bundle install bundle exec pod install --no-repo-update diff --git a/Podfile.lock b/Podfile.lock index 4f47d31..12d3a56 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,6 +1,6 @@ PODS: - - ICInputAccessory/KeyboardDismissTextField (1.3.0) - - ICInputAccessory/TokenField (1.3.0) + - ICInputAccessory/KeyboardDismissTextField (1.4.0) + - ICInputAccessory/TokenField (1.4.0) DEPENDENCIES: - ICInputAccessory/KeyboardDismissTextField (from `./`) @@ -11,7 +11,7 @@ EXTERNAL SOURCES: :path: "./" SPEC CHECKSUMS: - ICInputAccessory: e9142958152461ddb627e78e3f1f0a8b263b7271 + ICInputAccessory: dace41bea1ed8b0368bf8ffd439a215efa8effd3 PODFILE CHECKSUM: 6e8fb9f23fc92d92278fbf08ca8ef072ef28a486 diff --git a/README.md b/README.md index 97b2500..887331f 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Try . [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg)](https://github.com/Carthage/Carthage) [![CocoaPods Compatible](https://img.shields.io/cocoapods/v/ICInputAccessory.svg)](https://cocoapods.org/pods/ICInputAccessory) ![Platform](https://img.shields.io/cocoapods/p/ICInputAccessory.svg) -[![CocoaDocs](https://img.shields.io/cocoapods/metrics/doc-percent/ICInputAccessory.svg)](http://cocoadocs.org/docsets/ICInputAccessory/) +[![CocoaDocs](https://img.shields.io/cocoapods/metrics/doc-percent/ICInputAccessory.svg)](https://polydice.github.io/ICInputAccessory) ![Swift 3.0](https://img.shields.io/badge/Swift-3.0-orange.svg) ### ICKeyboardDismissTextField @@ -30,7 +30,7 @@ ICInputAccessory | iOS | Xcode | Swift `~> 1.0.0` | 8.0+ | 7.2 | ![Swift 2.1.1](https://img.shields.io/badge/Swift-2.1.1-orange.svg) `~> 1.1.0` | 8.0+ | 7.3 | ![Swift 2.2](https://img.shields.io/badge/Swift-2.2-orange.svg) `~> 1.2.0` | 8.0+ | 8.0 | ![Swift 2.3](https://img.shields.io/badge/Swift-2.3-orange.svg) -`~> 1.3.0` | 8.0+ | 8.0 | ![Swift 3.0](https://img.shields.io/badge/Swift-3.0-orange.svg) +`>= 1.3.0` | 8.0+ | 8.0 | ![Swift 3.0](https://img.shields.io/badge/Swift-3.0-orange.svg) ## Installation @@ -78,10 +78,10 @@ let tokenField = ICTokenField(frame: rect) tokenField.delegate = self as? ICTokenFieldDelegate ``` -* The characters that completes a token: +* The characters that complete a token: ```swift -/// Characters that completes a new token, defaults are whitespace and commas. +/// Characters that complete a new token, defaults are whitespace and commas. public var delimiters: [String] ``` @@ -132,29 +132,39 @@ See `Example/CustomizedTokenField.swift` for more details. `ICTokenField` currently notifies its delegate the following events: -* `tokenFieldDidBeginEditing(_:)` -* `tokenFieldDidEndEditing(_:)` -* `tokenFieldWillReturn(_:)` -* `tokenField(_:didEnterText:)` -* `tokenField(_:didDeleteText:atIndex:)` +```swift +@objc optional func tokenFieldDidBeginEditing(_ tokenField: ICTokenField) +@objc optional func tokenFieldDidEndEditing(_ tokenField: ICTokenField) +@objc optional func tokenFieldWillReturn(_ tokenField: ICTokenField) +@objc optional func tokenField(_ tokenField: ICTokenField, didChangeInputText text: String) +@objc optional func tokenField(_ tokenField: ICTokenField, shouldCompleteText text: String) -> Bool +@objc optional func tokenField(_ tokenField: ICTokenField, didCompleteText text: String) +@objc optional func tokenField(_ tokenField: ICTokenField, didDeleteText text: String, atIndex index: Int) +``` + +The displayed delimiter string can be customized by: + +```swift +@objc optional func tokenField(_ tokenField: ICTokenField, subsequentDelimiterForCompletedText text: String) -> String +``` ## Development -* Meke sure [Homebrew](http://brew.sh/) is installed. +* Make sure [Homebrew](http://brew.sh/) is installed. * Current `develop` branch requires Ruby `2.3.1`. * Set up dependencies by running the following command in the project root: ``` -make setup -``` + make bootstrap + ``` * Open **ICInputAccessory.xcworkspace** and run the demo app with the `Example` scheme. * See more tasks for building and testing: ``` -rake -T -``` + rake -T + ``` ## Contributing diff --git a/Source/TokenField/ICToken.swift b/Source/TokenField/ICToken.swift index 10ff293..818092e 100644 --- a/Source/TokenField/ICToken.swift +++ b/Source/TokenField/ICToken.swift @@ -63,10 +63,10 @@ class ICToken: UIView { // MARK: - Private Properties private(set) lazy var delimiterLabel: UILabel = { - let _delimiter = UILabel() - _delimiter.text = " , " - _delimiter.textColor = self.normalTextAttributes[NSForegroundColorAttributeName] as? UIColor - return _delimiter + let _label = UILabel() + _label.textColor = self.normalTextAttributes[NSForegroundColorAttributeName] as? UIColor + _label.textAlignment = .right + return _label }() private(set) lazy var textLabel: UILabel = { @@ -90,16 +90,15 @@ class ICToken: UIView { setUpSubviews() } - convenience init(text: String, normalAttributes: [String: NSObject]? = nil, highlightedAttributes: [String: NSObject]? = nil) { + convenience init(text: String, delimiter: String = ",", normalAttributes: [String: NSObject]? = nil, highlightedAttributes: [String: NSObject]? = nil) { self.init() if let attributes = normalAttributes { normalTextAttributes = attributes } if let attributes = highlightedAttributes { highlightedTextAttributes = attributes } - // didSet is not called within the initializer - setText(text) - } - - private func setText(_ text: String) { - self.text = text + delimiterLabel.text = delimiter + ({ + // Workaround to trigger didSet inside the initializer + self.text = text + })() } // MARK: - Private Methods @@ -127,7 +126,7 @@ class ICToken: UIView { "text": textLabel, "delimiter": delimiterLabel ] - addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[text][delimiter]|", + addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[text][delimiter]-5-|", options: [.alignAllCenterY], metrics: nil, views: views diff --git a/Source/TokenField/ICTokenField.swift b/Source/TokenField/ICTokenField.swift index eeed91c..4108f75 100644 --- a/Source/TokenField/ICTokenField.swift +++ b/Source/TokenField/ICTokenField.swift @@ -34,10 +34,16 @@ import UIKit @objc optional func tokenFieldDidEndEditing(_ tokenField: ICTokenField) /// Tells the delegate that the token field will process the pressing of the return button. @objc optional func tokenFieldWillReturn(_ tokenField: ICTokenField) + /// Tells the delegate the input text is changed. + @objc optional func tokenField(_ tokenField: ICTokenField, didChangeInputText text: String) + /// Asks the delegate if the text should become a token in the token field. + @objc optional func tokenField(_ tokenField: ICTokenField, shouldCompleteText text: String) -> Bool /// Tells the delegate that the text becomes a token in the token field. - @objc optional func tokenField(_ tokenField: ICTokenField, didEnterText text: String) + @objc optional func tokenField(_ tokenField: ICTokenField, didCompleteText text: String) /// Tells the delegate that the token at certain index is removed from the token field. @objc optional func tokenField(_ tokenField: ICTokenField, didDeleteText text: String, atIndex index: Int) + /// Asks the delegate for the subsequent delimiter string for a completed text in the token field. + @objc optional func tokenField(_ tokenField: ICTokenField, subsequentDelimiterForCompletedText text: String) -> String } @@ -287,23 +293,28 @@ open class ICTokenField: UIView, UITextFieldDelegate, ICBackspaceTextFieldDelega } let text = (input as NSString).replacingCharacters(in: range, with: string) + delegate?.tokenField?(self, didChangeInputText: text) for delimiter in delimiters { - if text.hasSuffix(delimiter) { - let index = text.index(text.endIndex, offsetBy: -delimiter.characters.count) - let newToken = text.substring(to: index) - textField.text = nil - - if !newToken.isEmpty && newToken != delimiter { - tokens.append(ICToken(text: newToken, normalAttributes: normalTokenAttributes, highlightedAttributes: highlightedTokenAttributes)) - layoutTokenTextField() - delegate?.tokenField?(self, didEnterText: newToken) - } - togglePlaceholderIfNeeded() + guard text.hasSuffix(delimiter) else { + continue + } - return false + let index = text.index(text.endIndex, offsetBy: -delimiter.characters.count) + let newText = text.substring(to: index) + + if !newText.isEmpty && newText != delimiter && (delegate?.tokenField?(self, shouldCompleteText: newText) ?? true) { + tokens.append(customizedToken(with: newText)) + layoutTokenTextField() + delegate?.tokenField?(self, didCompleteText: newText) } + + textField.text = nil + togglePlaceholderIfNeeded() + + return false } + return true } @@ -316,7 +327,7 @@ open class ICTokenField: UIView, UITextFieldDelegate, ICBackspaceTextFieldDelega // MARK: - ICBackspaceTextFieldDelegate - func textFieldShouldDelete(_ textField: ICBackspaceTextField) -> Bool { + @nonobjc func textFieldShouldDelete(_ textField: ICBackspaceTextField) -> Bool { if tokens.isEmpty { return true } @@ -364,6 +375,14 @@ open class ICTokenField: UIView, UITextFieldDelegate, ICBackspaceTextFieldDelega // MARK: - Private Methods + private func customizedToken(with text: String) -> ICToken { + if let string = delegate?.tokenField?(self, subsequentDelimiterForCompletedText: text) { + return ICToken(text: text, delimiter: string, normalAttributes: normalTokenAttributes, highlightedAttributes: highlightedTokenAttributes) + } else { + return ICToken(text: text, normalAttributes: normalTokenAttributes, highlightedAttributes: highlightedTokenAttributes) + } + } + /// Returns true if any highlighted token is found and removed, otherwise false. private func removeHighlightedToken() -> Bool { for (index, token) in tokens.enumerated() { @@ -436,10 +455,16 @@ open class ICTokenField: UIView, UITextFieldDelegate, ICBackspaceTextFieldDelega guard let text = inputTextField.text, !text.isEmpty else { return } + + let shouldCompleteText = delegate?.tokenField?(self, shouldCompleteText: text) ?? true + guard shouldCompleteText else { + return + } + inputTextField.text = nil - tokens.append(ICToken(text: text, normalAttributes: normalTokenAttributes, highlightedAttributes: highlightedTokenAttributes)) + tokens.append(customizedToken(with: text)) layoutTokenTextField() - delegate?.tokenField?(self, didEnterText: text) + delegate?.tokenField?(self, didCompleteText: text) } /// Removes the input text and all displayed tokens. diff --git a/scripts/update-docs.sh b/scripts/update-docs.sh index 28a174b..daf0471 100644 --- a/scripts/update-docs.sh +++ b/scripts/update-docs.sh @@ -17,7 +17,7 @@ git --no-pager diff --stat git add . git commit -m "[CI] Update documentation at $(date +'%Y-%m-%d %H:%M:%S %z')" -if [ "${TRAVIS_BRANCH}" = "master" ] && [ -n "$DANGER_GITHUB_API_TOKEN" ]; then +if [ "${TRAVIS_BRANCH}" = "develop" ] && [ -n "$DANGER_GITHUB_API_TOKEN" ]; then echo "Updating gh-pages..." git remote add upstream "https://${DANGER_GITHUB_API_TOKEN}@github.com/polydice/ICInputAccessory.git" git push --quiet upstream HEAD:gh-pages