diff --git a/AutocompleteTextfieldSwift/AutoCompleteTextField/AutoCompleteTextField.swift b/AutocompleteTextfieldSwift/AutoCompleteTextField/AutoCompleteTextField.swift index 5787bbd..14730e9 100644 --- a/AutocompleteTextfieldSwift/AutoCompleteTextField/AutoCompleteTextField.swift +++ b/AutocompleteTextfieldSwift/AutoCompleteTextField/AutoCompleteTextField.swift @@ -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.. 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.. 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) } } \ No newline at end of file diff --git a/AutocompleteTextfieldSwift/Controllers/ViewController.swift b/AutocompleteTextfieldSwift/Controllers/ViewController.swift index 3217175..d5ae9cc 100644 --- a/AutocompleteTextfieldSwift/Controllers/ViewController.swift +++ b/AutocompleteTextfieldSwift/Controllers/ViewController.swift @@ -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