Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
84 changes: 59 additions & 25 deletions SearchTextField/Classes/SearchTextField.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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 {
Expand All @@ -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]()
Expand All @@ -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 {
Expand All @@ -98,7 +101,7 @@ open class SearchTextField: UITextField {
open var startSuggestingInmediately = false

open var comparisonOptions: NSString.CompareOptions = [.caseInsensitive]

////////////////////////////////////////////////////////////////////////
// Private implementation

Expand All @@ -125,7 +128,7 @@ open class SearchTextField: UITextField {
deinit {
NotificationCenter.default.removeObserver(self)
}

override open func willMove(toSuperview newSuperview: UIView?) {
super.willMove(toSuperview: newSuperview)

Expand All @@ -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)
Expand All @@ -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 {
Expand Down Expand Up @@ -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
Expand All @@ -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!)
}
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -320,25 +325,34 @@ 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() {
clearResults()
tableView?.reloadData()
placeholderLabel?.attributedText = nil
}

open func textFieldDidEndEditingOnExit() {
if let firstElement = filteredResults.first {
if let itemSelectionHandler = self.itemSelectionHandler {
Expand All @@ -355,7 +369,8 @@ open class SearchTextField: UITextField {
}
}
}



fileprivate func filter(_ addAll: Bool) {
clearResults()

Expand Down Expand Up @@ -399,7 +414,7 @@ open class SearchTextField: UITextField {
}
}
}

tableView?.reloadData()

if inlineMode {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -521,7 +558,6 @@ extension SearchTextField: UITableViewDelegate, UITableViewDataSource {

////////////////////////////////////////////////////////////////////////
// Search Text Field Theme

public struct SearchTextFieldTheme {
public var cellHeight: CGFloat
public var bgColor: UIColor
Expand Down Expand Up @@ -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?
Expand All @@ -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
}
Expand All @@ -582,7 +617,6 @@ public typealias SearchTextFieldItemHandler = (_ filteredResults: [SearchTextFie

////////////////////////////////////////////////////////////////////////
// Suggestions List Direction

enum Direction {
case down
case up
Expand Down