diff --git a/README.md b/README.md index a3fec7f..1354a01 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,9 @@ You can decide wether to show the list as soon as the field is focused or when t You can also detects when the user stops typing, very useful when you can get a suggestion list from a remote server. **New Feature!** -Now you can make suggestions "inline", showing the first matched result as the placeholder (instead of the results list) and selecting it when the user touches the enter key. +Now you can: +- make suggestions "inline", showing the first matched result as the placeholder (instead of the results list) and selecting it when the user touches the enter key: +- activate the automatic text resize via _resizableText_ boolean. ------ ![alt_tag](https://raw.githubusercontent.com/apasccon/SearchTextField/master/Example/SearchTextField/SearchTextField_Demo.gif) diff --git a/SearchTextField/Classes/SearchTextField.swift b/SearchTextField/Classes/SearchTextField.swift index 3c461e9..a40ba52 100644 --- a/SearchTextField/Classes/SearchTextField.swift +++ b/SearchTextField/Classes/SearchTextField.swift @@ -5,17 +5,16 @@ // Created by Alejandro Pasccon on 4/20/16. // Copyright © 2016 Alejandro Pasccon. All rights reserved. // - import UIKit open class SearchTextField: UITextField { - + //////////////////////////////////////////////////////////////////////// // Public interface /// Maximum number of results to be shown in the suggestions list open var maxNumberOfResults = 0 - + /// Maximum height of the results list open var maxResultsListHeight = 0 @@ -24,7 +23,7 @@ open class SearchTextField: UITextField { /// Indicate if keyboard is showing or not open var keyboardIsShowing = false - + /// Set your custom visual theme, or just choose between pre-defined SearchTextFieldTheme.lightTheme() and SearchTextFieldTheme.darkTheme() themes open var theme = SearchTextFieldTheme.lightTheme() { didSet { @@ -47,7 +46,7 @@ open class SearchTextField: UITextField { open func filterItems(_ items: [SearchTextFieldItem]) { filterDataSource = items } - + /// Set an array of strings to be used for suggestions open func filterStrings(_ strings: [String]) { var items = [SearchTextFieldItem]() @@ -67,19 +66,23 @@ open class SearchTextField: UITextField { /// Set your custom set of attributes in order to highlight the string found in each item open var highlightAttributes: [String: AnyObject] = [NSFontAttributeName:UIFont.boldSystemFont(ofSize: 10)] - + + /// Automatically resize the text + open var resizableText: Bool = false + var originalFontSize: CGFloat = 0 + /// Start showing the default loading indicator, useful for searches that take some time. open func showLoadingIndicator() { self.rightViewMode = .always indicator.startAnimating() } - + /// Hide the default loading indicator open func stopLoadingIndicator() { self.rightViewMode = .never indicator.stopAnimating() } - + /// When InlineMode is true, the suggestions appear in the same line than the entered string. It's useful for email domains suggestion for example. open var inlineMode: Bool = false { didSet { @@ -98,7 +101,7 @@ open class SearchTextField: UITextField { open var startSuggestingInmediately = false open var comparisonOptions: NSString.CompareOptions = [.caseInsensitive] - + //////////////////////////////////////////////////////////////////////// // Private implementation @@ -125,7 +128,7 @@ open class SearchTextField: UITextField { deinit { NotificationCenter.default.removeObserver(self) } - + override open func willMove(toSuperview newSuperview: UIView?) { super.willMove(toSuperview: newSuperview) @@ -134,6 +137,7 @@ open class SearchTextField: UITextField { self.addTarget(self, action: #selector(SearchTextField.textFieldDidEndEditing), for: .editingDidEnd) self.addTarget(self, action: #selector(SearchTextField.textFieldDidEndEditingOnExit), for: .editingDidEndOnExit) + NotificationCenter.default.addObserver(self, selector: #selector(SearchTextField.keyboardWillShow(_:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(SearchTextField.keyboardWillHide(_:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(SearchTextField.keyboardDidChangeFrame(_:)), name: NSNotification.Name.UIKeyboardDidChangeFrame, object: nil) @@ -151,6 +155,7 @@ open class SearchTextField: UITextField { // Create the loading indicator indicator.hidesWhenStopped = true self.rightView = indicator + } override open func rightViewRect(forBounds bounds: CGRect) -> CGRect { @@ -186,14 +191,14 @@ open class SearchTextField: UITextField { var newRect = self.placeholderRect(forBounds: self.bounds) var caretRect = self.caretRect(for: self.beginningOfDocument) let textRect = self.textRect(forBounds: self.bounds) - + if let range = textRange(from: beginningOfDocument, to: endOfDocument) { caretRect = self.firstRect(for: range) } newRect.origin.x = caretRect.origin.x + caretRect.size.width + textRect.origin.x newRect.size.width = newRect.size.width - newRect.origin.x - + if let placeholderLabel = placeholderLabel { placeholderLabel.font = self.font placeholderLabel.frame = newRect @@ -202,13 +207,13 @@ open class SearchTextField: UITextField { placeholderLabel?.font = self.font placeholderLabel?.backgroundColor = UIColor.clear placeholderLabel?.lineBreakMode = .byClipping - + if let placeholderColor = self.attributedPlaceholder?.attribute(NSForegroundColorAttributeName, at: 0, effectiveRange: nil) as? UIColor { placeholderLabel?.textColor = placeholderColor } else { placeholderLabel?.textColor = UIColor ( red: 0.8, green: 0.8, blue: 0.8, alpha: 1.0 ) } - + self.addSubview(placeholderLabel!) } } @@ -263,7 +268,7 @@ open class SearchTextField: UITextField { if self.isFirstResponder { superview?.bringSubview(toFront: self) } - + tableView.layer.borderColor = theme.borderColor.cgColor tableView.layer.cornerRadius = 2 tableView.separatorColor = theme.separatorColor @@ -320,17 +325,26 @@ open class SearchTextField: UITextField { } else { filter(false) prepareDrawTableResult() + + if resizableText{ + self.resizeText() + } } buildPlaceholderLabel() } + open func textFieldDidBeginEditing() { if startVisible && text!.isEmpty { clearResults() filter(true) } placeholderLabel?.attributedText = nil + + if let font = self.font{ + self.originalFontSize = font.pointSize + } } open func textFieldDidEndEditing() { @@ -338,7 +352,7 @@ open class SearchTextField: UITextField { tableView?.reloadData() placeholderLabel?.attributedText = nil } - + open func textFieldDidEndEditingOnExit() { if let firstElement = filteredResults.first { if let itemSelectionHandler = self.itemSelectionHandler { @@ -355,7 +369,8 @@ open class SearchTextField: UITextField { } } } - + + fileprivate func filter(_ addAll: Bool) { clearResults() @@ -399,7 +414,7 @@ open class SearchTextField: UITextField { } } } - + tableView?.reloadData() if inlineMode { @@ -462,6 +477,23 @@ open class SearchTextField: UITextField { redrawSearchTableView() } } + + //Resizes the text until fits + internal func resizeText() { + if let text = self.text{ + self.font = UIFont.systemFont(ofSize: originalFontSize) + let textString = text as NSString + var widthOfText = textString.size(attributes: [NSFontAttributeName : self.font!]).width + var widthOfFrame = self.frame.size.width + // decrease font size until it fits + while widthOfFrame - 5 < widthOfText { + let fontSize = self.font!.pointSize + self.font = self.font?.withSize(fontSize - 0.5) + widthOfText = textString.size(attributes: [NSFontAttributeName : self.font!]).width + widthOfFrame = self.frame.size.width + } + } + } } extension SearchTextField: UITableViewDelegate, UITableViewDataSource { @@ -495,7 +527,7 @@ extension SearchTextField: UITableViewDelegate, UITableViewDataSource { cell!.detailTextLabel?.text = filteredResults[(indexPath as NSIndexPath).row].subtitle cell!.textLabel?.attributedText = filteredResults[(indexPath as NSIndexPath).row].attributedTitle cell!.detailTextLabel?.attributedText = filteredResults[(indexPath as NSIndexPath).row].attributedSubtitle - + cell!.imageView?.image = filteredResults[(indexPath as NSIndexPath).row].image cell!.selectionStyle = .none @@ -510,6 +542,11 @@ extension SearchTextField: UITableViewDelegate, UITableViewDataSource { public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if itemSelectionHandler == nil { self.text = filteredResults[(indexPath as NSIndexPath).row].title + + if resizableText{ + self.resizeText() + } + } else { let index = indexPath.row itemSelectionHandler!(filteredResults, index) @@ -521,7 +558,6 @@ extension SearchTextField: UITableViewDelegate, UITableViewDataSource { //////////////////////////////////////////////////////////////////////// // Search Text Field Theme - public struct SearchTextFieldTheme { public var cellHeight: CGFloat public var bgColor: UIColor @@ -551,12 +587,11 @@ public struct SearchTextFieldTheme { //////////////////////////////////////////////////////////////////////// // Filter Item - public struct SearchTextFieldItem { // Private vars fileprivate var attributedTitle: NSMutableAttributedString? fileprivate var attributedSubtitle: NSMutableAttributedString? - + // Public interface public var title: String public var subtitle: String? @@ -567,12 +602,12 @@ public struct SearchTextFieldItem { self.subtitle = subtitle self.image = image } - + public init(title: String, subtitle: String?) { self.title = title self.subtitle = subtitle } - + public init(title: String) { self.title = title } @@ -582,7 +617,6 @@ public typealias SearchTextFieldItemHandler = (_ filteredResults: [SearchTextFie //////////////////////////////////////////////////////////////////////// // Suggestions List Direction - enum Direction { case down case up