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
Original file line number Diff line number Diff line change
Expand Up @@ -9,196 +9,178 @@
import Foundation
import UIKit

//MARK:- Interface
public class AutoCompleteTextField:UITextField {
/// Manages the instance of tableview
private var autoCompleteTableView:UITableView?
/// Holds the collection of attributed strings
private lazy var attributedAutoCompleteStrings = [NSAttributedString]()
/// Handles user selection action on autocomplete table view
public var onSelect:(String, NSIndexPath)->() = {_,_ in}
/// Handles textfield's textchanged
public var onTextChange:(String)->() = {_ in}

/// Font for the text suggestions
public var autoCompleteTextFont = UIFont.systemFontOfSize(12)
/// Color of the text suggestions
public var autoCompleteTextColor = UIColor.blackColor()
/// Used to set the height of cell for each suggestions
public var autoCompleteCellHeight:CGFloat = 44.0
/// The maximum visible suggestion
public var maximumAutoCompleteCount = 3
/// Used to set your own preferred separator inset
public var autoCompleteSeparatorInset = UIEdgeInsetsZero
/// Shows autocomplete text with formatting
public var enableAttributedText = false
/// User Defined Attributes
public var autoCompleteAttributes:[String:AnyObject]?
/// Hides autocomplete tableview after selecting a suggestion
public var hidesWhenSelected = true
/// Hides autocomplete tableview when the textfield is empty
public var hidesWhenEmpty:Bool?{
didSet{
assert(hidesWhenEmpty != nil, "hideWhenEmpty cannot be set to nil")
autoCompleteTableView?.hidden = hidesWhenEmpty!
}
}
/// The table view height
public var autoCompleteTableHeight:CGFloat?{
didSet{
redrawTable()
}
}
/// The strings to be shown on as suggestions, setting the value of this automatically reload the tableview
public var autoCompleteStrings:[String]?{
didSet{ reload() }
}


//MARK: - Init
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
setupAutocompleteTable(superview!)
}

public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}

public override func awakeFromNib() {
super.awakeFromNib()
commonInit()
setupAutocompleteTable(superview!)
}

public override func willMoveToSuperview(newSuperview: UIView?) {
super.willMoveToSuperview(newSuperview)
commonInit()
setupAutocompleteTable(newSuperview!)
}

private func commonInit(){
hidesWhenEmpty = true
autoCompleteAttributes = [NSForegroundColorAttributeName:UIColor.blackColor()]
autoCompleteAttributes![NSFontAttributeName] = UIFont.boldSystemFontOfSize(12)
self.clearButtonMode = .Always
self.addTarget(self, action: "textFieldDidChange", forControlEvents: .EditingChanged)
self.addTarget(self, action: "textFieldDidEndEditing", forControlEvents: .EditingDidEnd)
}

private func setupAutocompleteTable(view:UIView){
let screenSize = UIScreen.mainScreen().bounds.size
let tableView = UITableView(frame: CGRectMake(self.frame.origin.x, self.frame.origin.y + CGRectGetHeight(self.frame), screenSize.width - (self.frame.origin.x * 2), 30.0))
tableView.dataSource = self
tableView.delegate = self
tableView.rowHeight = autoCompleteCellHeight
tableView.hidden = hidesWhenEmpty ?? true
view.addSubview(tableView)
autoCompleteTableView = tableView

autoCompleteTableHeight = 100.0
}

private func redrawTable(){
if let autoCompleteTableView = autoCompleteTableView, let autoCompleteTableHeight = autoCompleteTableHeight {
var newFrame = autoCompleteTableView.frame
newFrame.size.height = autoCompleteTableHeight
autoCompleteTableView.frame = newFrame
}
}

//MARK: - Private Methods
private func reload(){
if enableAttributedText{
let attrs = [NSForegroundColorAttributeName:autoCompleteTextColor, NSFontAttributeName:UIFont.systemFontOfSize(12.0)]

if attributedAutoCompleteStrings.count > 0 {
attributedAutoCompleteStrings.removeAll(keepCapacity: false)
}

if let autoCompleteStrings = autoCompleteStrings, let autoCompleteAttributes = autoCompleteAttributes {
for i in 0..<autoCompleteStrings.count{
let str = autoCompleteStrings[i] as NSString
let range = str.rangeOfString(text!, options: .CaseInsensitiveSearch)
let attString = NSMutableAttributedString(string: autoCompleteStrings[i], attributes: attrs)
attString.addAttributes(autoCompleteAttributes, range: range)
attributedAutoCompleteStrings.append(attString)
}
}
}
autoCompleteTableView?.reloadData()
}

func textFieldDidChange(){
guard let _ = text else {
return
}

onTextChange(text!)
if text!.isEmpty{ autoCompleteStrings = nil }
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.autoCompleteTableView?.hidden = self.hidesWhenEmpty! ? self.text!.isEmpty : false
})
}

func textFieldDidEndEditing() {
autoCompleteTableView?.hidden = true
}
//MARK: Properties
//Outlets
@IBOutlet weak var presenterView: UIView!

//Inspectables
@IBInspectable public var hidesWhenSelected: Bool = true
@IBInspectable public var maximumAutoCompleteCount: Int = 4
@IBInspectable public var enableAttributedText: Bool = false
@IBInspectable public var autoCompleteCellHeight: CGFloat = 44
@IBInspectable public var autoCompleteSeparatorInset: UIEdgeInsets = UIEdgeInsetsZero
@IBInspectable public var autoCompleteTextColor: UIColor = .darkGrayColor()
@IBInspectable public var hidesWhenEmpty: Bool = true { didSet { autoCompleteTableView?.hidden = hidesWhenEmpty } }

//Variables
private var autoCompleteTableView: UITableView?
public var autoCompleteAttributes: [String: AnyObject]?
public var onTextChange: ((String) -> ()) = { _ in }
public var onSelect: ((String, NSIndexPath) -> ()) = { _, _ in }
private lazy var attributedAutoCompleteStrings = [NSAttributedString] ()
public var autoCompleteTableHeight: CGFloat = (4 * 44) { didSet { redrawTable() } }
public var autoCompleteStrings: [String]? {
didSet {
reload()
autoCompleteTableHeight = CGFloat(min(maximumAutoCompleteCount, autoCompleteStrings?.count ?? 0)) * autoCompleteCellHeight
redrawTable()
}
}

//Constants
let reuseIdentifier = "autocompleteCellReuseIdentifier"

//MARK:- Initialisation
override init(frame: CGRect) {
super.init(frame:frame)
commonInit()
}

public required init?(coder aDecorder: NSCoder) { super.init(coder: aDecorder) }
}

//MARK:- Implementation
extension AutoCompleteTextField {
//MARK: System
public override func awakeFromNib() {
super.awakeFromNib()
commonInit()
}
}

extension AutoCompleteTextField {
//MARK: Common Init
private func commonInit() {
//Initial Configuration
autoCompleteAttributes = [NSForegroundColorAttributeName: UIColor.blackColor(), NSFontAttributeName: UIFont.boldSystemFontOfSize(12)]
addTarget(self, action: #selector(textFieldDidChange), forControlEvents: .EditingChanged)
addTarget(self, action: #selector(textFieldDidEndEditing), forControlEvents: .EditingDidEnd)

//Setup Table
performClosureAsynchoronouslyOnMainThread() { self.setupAutocompleteTable() }
}
}

extension AutoCompleteTextField {
//MARK: Handle Text Input
func textFieldDidChange() {
guard let text = text else { return }

onTextChange(text)
if text.isEmpty { autoCompleteStrings = nil }
performClosureAsynchoronouslyOnMainThread() { self.autoCompleteTableView?.hidden = self.hidesWhenEmpty ? text.isEmpty : false }
}

func textFieldDidEndEditing() {
autoCompleteTableView?.hidden = true
}
}

extension AutoCompleteTextField {
//MARK: Configure Table
//Setup Table
@objc private func setupAutocompleteTable() {
//Configure Table Frame
autoCompleteTableView = UITableView(frame: CGRectZero, style: .Plain)
if let presenterView = presenterView, convertedPoint = self.superview?.convertPoint(CGPoint(x: CGRectGetMinX(self.frame), y: CGRectGetMaxY(self.frame)), toView: presenterView), convertedFrame = CGRect(origin: convertedPoint, size: CGSize(width: self.frame.width, height: 0)) as CGRect? {
autoCompleteTableView?.frame = convertedFrame
}

//Configure Table Properties
autoCompleteTableView?.dataSource = self
autoCompleteTableView?.delegate = self
autoCompleteTableView?.rowHeight = autoCompleteCellHeight
autoCompleteTableView?.hidden = hidesWhenEmpty ?? true

//Add Table To Presenter View
if autoCompleteTableView != nil { presenterView?.addSubview(autoCompleteTableView!) }

//Set Table Height
autoCompleteTableHeight = CGFloat(0)
}

//Perform Custom Table Reload
private func reload() {
//Preare Reload
if enableAttributedText == true {
let attrs = [NSForegroundColorAttributeName: autoCompleteTextColor, NSFontAttributeName: UIFont.boldSystemFontOfSize(12.0)]
if attributedAutoCompleteStrings.count > 0 { attributedAutoCompleteStrings.removeAll(keepCapacity: false) }
if let autoCompleteStrings = autoCompleteStrings, autoCompleteAttributes = autoCompleteAttributes {
for i in 0..<autoCompleteStrings.count {
let str = autoCompleteStrings[i] as NSString
let range = str.rangeOfString(text!, options: . CaseInsensitiveSearch)
let attString = NSMutableAttributedString(string: autoCompleteStrings[i], attributes: attrs)
attString.addAttributes(autoCompleteAttributes, range: range)
attributedAutoCompleteStrings.append(attString)
}
}
}
//Perform Reload
autoCompleteTableView?.reloadData()
}

//Redraw Table On Screen
private func redrawTable() {
guard autoCompleteTableView != nil else { return }

//Update Table Height
var newFrame = autoCompleteTableView!.frame
newFrame.size.height = autoCompleteTableHeight
UIView.animateWithDuration(0.3) { self.autoCompleteTableView?.frame = newFrame }
}
}

//MARK: - UITableViewDataSource - UITableViewDelegate
extension AutoCompleteTextField: UITableViewDataSource, UITableViewDelegate {

public func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return autoCompleteStrings != nil ? (autoCompleteStrings!.count > maximumAutoCompleteCount ? maximumAutoCompleteCount : autoCompleteStrings!.count) : 0
}

public func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "autocompleteCellIdentifier"
var cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier)
if cell == nil{
cell = UITableViewCell(style: .Default, reuseIdentifier: cellIdentifier)
}

if enableAttributedText{
cell?.textLabel?.attributedText = attributedAutoCompleteStrings[indexPath.row]
}
else{
cell?.textLabel?.font = autoCompleteTextFont
cell?.textLabel?.textColor = autoCompleteTextColor
cell?.textLabel?.text = autoCompleteStrings![indexPath.row]
}

cell?.contentView.gestureRecognizers = nil
return cell!
}

public func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let cell = tableView.cellForRowAtIndexPath(indexPath)

if let selectedText = cell?.textLabel?.text {
self.text = selectedText
onSelect(selectedText, indexPath)
}

dispatch_async(dispatch_get_main_queue(), { () -> Void in
tableView.hidden = self.hidesWhenSelected
})
}

public func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
if cell.respondsToSelector("setSeparatorInset:"){
cell.separatorInset = autoCompleteSeparatorInset
}
if cell.respondsToSelector("setPreservesSuperviewLayoutMargins:"){
cell.preservesSuperviewLayoutMargins = false
}
if cell.respondsToSelector("setLayoutMargins:"){
cell.layoutMargins = autoCompleteSeparatorInset
}
}

public func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return autoCompleteCellHeight
}
//MARK: Table Data Source And Delegates
public func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return autoCompleteStrings != nil ? (autoCompleteStrings?.count > maximumAutoCompleteCount ? maximumAutoCompleteCount : autoCompleteStrings!.count) : 0
}

public func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return autoCompleteCellHeight
}

public func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
if cell.respondsToSelector(Selector("setSeparatorInset:")) { cell.separatorInset = autoCompleteSeparatorInset }
if cell.respondsToSelector(Selector("setPreservesSuperviewLayoutMargins:")) { cell.preservesSuperviewLayoutMargins = false }
if cell.respondsToSelector(Selector("setLayoutMargins:")) { cell.layoutMargins = autoCompleteSeparatorInset }
}

public func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(reuseIdentifier) ?? UITableViewCell(style: .Default, reuseIdentifier: reuseIdentifier)
cell.contentView.gestureRecognizers = nil
if enableAttributedText {
cell.textLabel?.attributedText = attributedAutoCompleteStrings[indexPath.row]
} else {
cell.textLabel?.font = self.font
cell.textLabel?.textColor = autoCompleteTextColor
cell.textLabel?.text = autoCompleteStrings![indexPath.row]
}
return cell
}

public func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if let cell = tableView.cellForRowAtIndexPath(indexPath) as UITableViewCell?, selectedText = cell.textLabel?.text {
self.text = selectedText
onSelect(selectedText, indexPath)
}
performClosureAsynchoronouslyOnMainThread() { tableView.hidden = self.hidesWhenSelected }
}
}

extension AutoCompleteTextField {
//MARK: Additionals
func performClosureAsynchoronouslyOnMainThread(closure: () -> ()) { dispatch_async(dispatch_get_main_queue(), closure) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class ViewController: UIViewController {

private func configureTextField(){
autocompleteTextfield.autoCompleteTextColor = UIColor(red: 128.0/255.0, green: 128.0/255.0, blue: 128.0/255.0, alpha: 1.0)
autocompleteTextfield.autoCompleteTextFont = UIFont(name: "HelveticaNeue-Light", size: 12.0)!
//autocompleteTextfield.autoCompleteTextFont = UIFont(name: "HelveticaNeue-Light", size: 12.0)!
autocompleteTextfield.autoCompleteCellHeight = 35.0
autocompleteTextfield.maximumAutoCompleteCount = 20
autocompleteTextfield.hidesWhenSelected = true
Expand Down