Last active
July 11, 2020 16:25
-
-
Save thanhit93/0bd667ae7892a7c07453336fb15f79ca to your computer and use it in GitHub Desktop.
AutoCompleteTextField in swift 4.2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// AutoCompleteTextField.swift | |
// ABC_Project | |
// | |
// Created by Trương Thành on 7/11/20. | |
// Copyright © 2020 education. All rights reserved. | |
// | |
import Foundation | |
import UIKit | |
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, IndexPath)->() = {_,_ in} | |
/// Handles textfield's textchanged | |
public var onTextChange:(String)->() = {_ in} | |
/// Font for the text suggestions | |
public var autoCompleteTextFont = UIFont.systemFont(ofSize: 12) | |
/// Color of the text suggestions | |
public var autoCompleteTextColor = UIColor.black | |
/// 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 = UIEdgeInsets.zero | |
/// Shows autocomplete text with formatting | |
public var enableAttributedText = false | |
/// User Defined Attributes | |
public var autoCompleteAttributes:[NSAttributedString.Key : Any]? | |
/// 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?.isHidden = 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(view: superview!) | |
} | |
public required init?(coder aDecoder: NSCoder) { | |
super.init(coder: aDecoder) | |
} | |
public override func awakeFromNib() { | |
super.awakeFromNib() | |
commonInit() | |
setupAutocompleteTable(view: superview!) | |
} | |
public override func willMove(toSuperview newSuperview: UIView?) { | |
super.willMove(toSuperview: newSuperview) | |
if let newSuperView = newSuperview { | |
commonInit() | |
setupAutocompleteTable(view: newSuperView) | |
} | |
} | |
private func commonInit(){ | |
hidesWhenEmpty = true | |
autoCompleteAttributes = [NSAttributedString.Key.foregroundColor:UIColor.black] | |
autoCompleteAttributes![NSAttributedString.Key.font] = UIFont.boldSystemFont(ofSize: 12) | |
self.clearButtonMode = .always | |
self.addTarget(self, action: #selector(textFieldDidChange), for: .editingChanged) | |
self.addTarget(self, action: #selector(textFieldDidEndEditing), for: .editingDidEnd) | |
} | |
private func setupAutocompleteTable(view:UIView){ | |
let screenSize = UIScreen.main.bounds.size | |
// let tableView = UITableView(frame: CGRect(x: self.frame.origin.x, y: self.frame.origin.y + self.frame.height, width: screenSize.width - (self.frame.origin.x * 2), height: 30.0)) | |
let tableView = UITableView(frame: CGRect(x: self.frame.origin.x, y: self.frame.origin.y + self.frame.height, width: screenSize.width - (self.frame.origin.x + self.frame.origin.x / 2.0), height: 30.0)) | |
tableView.dataSource = self | |
tableView.delegate = self | |
tableView.rowHeight = autoCompleteCellHeight | |
tableView.isHidden = 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 = [NSAttributedString.Key.foregroundColor:autoCompleteTextColor, NSAttributedString.Key.font:autoCompleteTextFont] | |
if attributedAutoCompleteStrings.count > 0 { | |
attributedAutoCompleteStrings.removeAll(keepingCapacity: false) | |
} | |
if let autoCompleteStrings = autoCompleteStrings, let autoCompleteAttributes = autoCompleteAttributes { | |
for i in 0..<autoCompleteStrings.count{ | |
let str = autoCompleteStrings[i] as NSString | |
let range = str.range(of: text!, options: .caseInsensitive) | |
let attString = NSMutableAttributedString(string: autoCompleteStrings[i], attributes: attrs) | |
attString.addAttributes(autoCompleteAttributes, range: range) | |
attributedAutoCompleteStrings.append(attString) | |
} | |
} | |
} | |
autoCompleteTableView?.reloadData() | |
} | |
@objc func textFieldDidChange() { | |
guard let _ = text else { | |
return | |
} | |
onTextChange(text!) | |
if text!.isEmpty{ autoCompleteStrings = nil } | |
DispatchQueue.main.async { | |
self.autoCompleteTableView?.isHidden = self.hidesWhenEmpty! ? self.text!.isEmpty : false | |
} | |
} | |
@objc func textFieldDidEndEditing() { | |
autoCompleteTableView?.isHidden = true | |
} | |
} | |
//MARK: - UITableViewDataSource - UITableViewDelegate | |
extension AutoCompleteTextField: UITableViewDataSource, UITableViewDelegate { | |
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { | |
let cellIdentifier = "autocompleteCellIdentifier" | |
var cell = tableView.dequeueReusableCell(withIdentifier: 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, numberOfRowsInSection section: Int) -> Int { | |
return autoCompleteStrings != nil ? (autoCompleteStrings!.count > maximumAutoCompleteCount ? maximumAutoCompleteCount : autoCompleteStrings!.count) : 0 | |
} | |
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { | |
let cell = tableView.cellForRow(at: indexPath as IndexPath) | |
if let selectedText = cell?.textLabel?.text { | |
self.text = selectedText | |
onSelect(selectedText, indexPath) | |
} | |
DispatchQueue.main.async { | |
tableView.isHidden = self.hidesWhenSelected | |
} | |
} | |
public func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { | |
if (cell.responds(to: #selector(setter: UITableViewCell.separatorInset))) { | |
cell.separatorInset = autoCompleteSeparatorInset | |
} | |
if (cell.responds(to: #selector(setter: UIView.preservesSuperviewLayoutMargins))) { | |
cell.preservesSuperviewLayoutMargins = false | |
} | |
if (cell.responds(to: #selector(setter: UIView.layoutMargins))) { | |
cell.layoutMargins = autoCompleteSeparatorInset | |
} | |
} | |
public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { | |
return autoCompleteCellHeight | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Usage:
Result: