Last active
April 10, 2023 07:02
-
-
Save AshvinGudaliya/264e2ff5f505ff1c4d8b3f2a24c61779 to your computer and use it in GitHub Desktop.
A simple subclass of UITextField for getting auto complete place search from Google Places.
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
// | |
// AGAutocompletePlaceTextfield.swift | |
// AGAutocompletePlaceTextfield | |
// | |
// Created by Arpit Jain on 28/07/17. | |
// Copyright © 2017 Arpit Jain. All rights reserved. | |
// | |
import UIKit | |
import GooglePlaces | |
class AGAutocompletePlaceTextfield: UITextField , UITextFieldDelegate { | |
//MARK: - PROPERTIES | |
struct AGFilterPlace { | |
let id: String | |
let name: String | |
} | |
let placesClient = GMSPlacesClient() | |
/// Instance of autoCompleteTableView | |
fileprivate var tableView: UITableView? | |
/// Gives the array of autoCompletePlacesData | |
open var autoCompletePlacesData = [AGFilterPlace]() | |
/// Gives the selected place and indexPath | |
open var selectedPlace: ((GMSPlace) -> ())? | |
/// Font for the places data | |
open var autoCompleteTextFont = UIFont.systemFont(ofSize: 12) | |
/// Color of the places data | |
open var autoCompleteTextColor = UIColor.black | |
/// Used to set the height of cell for each suggestions | |
open var autoCompleteCellHeight: CGFloat = 44.0 | |
/// The maximum visible suggestion | |
open var maxAutoCompleteDataCount = 5 | |
/// Used to set your own preferred separator inset | |
open var autoCompleteSeparatorInset = UIEdgeInsets.zero | |
/// Hides autoCompleteTableView after selecting a place | |
open var hidesWhenSelected = true | |
/// Shows autoCompletePlacesData with formatting | |
open var highLightTypeTextedEnabled: Bool? | |
/// Define attributes for highlighted text | |
open var highLightTypeTextedAttributes: [NSAttributedStringKey: Any]? | |
/// The autoCompleteTableView height | |
open var autoCompleteTableHeight: CGFloat?{ | |
didSet{ | |
redrawTable() | |
} | |
} | |
/// this is the x margin for table view from textfield | |
open var autoCompleteTableMargin = CGFloat() | |
//MARK: - INIT FUNCTIONS | |
public required init?(coder aDecoder: NSCoder) { | |
super.init(coder: aDecoder) | |
} | |
open override func awakeFromNib() { | |
super.awakeFromNib() | |
commonInit() | |
setupAutocompleteTable(superview!) | |
} | |
override func layoutSubviews() { | |
super.layoutSubviews() | |
commonInit() | |
setupAutocompleteTable(superview!) | |
} | |
open override func willMove(toSuperview newSuperview: UIView?) { | |
super.willMove(toSuperview: newSuperview) | |
commonInit() | |
setupAutocompleteTable(newSuperview!) | |
} | |
fileprivate func commonInit(){ | |
tableView?.isHidden = true | |
highLightTypeTextedEnabled = false | |
highLightTypeTextedAttributes = [NSAttributedStringKey.foregroundColor: UIColor.black] | |
highLightTypeTextedAttributes![NSAttributedStringKey.font] = UIFont.boldSystemFont(ofSize: 12) | |
self.clearButtonMode = .always | |
self.addTarget(self, action: #selector(AGAutocompletePlaceTextfield.textFieldDidChange), for: .editingChanged) | |
self.addTarget(self, action: #selector(AGAutocompletePlaceTextfield.textFieldDidEndEditing), for: .editingDidEnd) | |
} | |
fileprivate func setupAutocompleteTable(_ view:UIView){ | |
autoCompleteTableMargin = 10.0 | |
tableView = UITableView(frame: CGRect(x: self.frame.origin.x + autoCompleteTableMargin/2, y: self.frame.origin.y + self.frame.height, width: (self.frame.width - autoCompleteTableMargin), height: 30.0)) | |
tableView?.dataSource = self | |
tableView?.delegate = self | |
tableView?.rowHeight = autoCompleteCellHeight | |
tableView?.isHidden = true | |
tableView?.layer.borderColor = UIColor.lightGray.cgColor | |
tableView?.layer.borderWidth = 0.5 | |
tableView?.layer.zPosition = CGFloat(Float.greatestFiniteMagnitude) | |
view.addSubview(tableView!) | |
autoCompleteTableHeight = 200.0 | |
} | |
fileprivate func redrawTable(){ | |
if let tableView = tableView, let autoCompleteTableHeight = autoCompleteTableHeight { | |
var newFrame = tableView.frame | |
newFrame.size.height = autoCompleteTableHeight | |
tableView.frame = newFrame | |
} | |
} | |
//MARK: - Private Methodss | |
fileprivate func showHighlightedTypedText(aStr : NSString) -> NSAttributedString { | |
let attrs = [NSAttributedStringKey.foregroundColor: autoCompleteTextColor, NSAttributedStringKey.font: autoCompleteTextFont] | |
let range = aStr.range(of: text!, options: .caseInsensitive) | |
let attString = NSMutableAttributedString(string: aStr as String, attributes: attrs) | |
attString.addAttributes(highLightTypeTextedAttributes!, range: range) | |
autoCompleteTextColor = UIColor.gray | |
return attString | |
} | |
//MARK: - AJAutoCompleteTextfieldDelegate | |
@objc func textFieldDidChange(){ | |
placesClient.autocompleteQuery(text ?? "", bounds: nil, filter: nil, callback: { (results, error) in | |
DispatchQueue.main.async | |
{ | |
self.autoCompletePlacesData.removeAll() | |
if results != nil { | |
for result in results!{ | |
if let placeId = result.placeID { | |
self.autoCompletePlacesData.append(AGFilterPlace(id: placeId , name: result.attributedFullText.string)) | |
} | |
} | |
} | |
if self.autoCompletePlacesData.count != 0{ | |
self.tableView?.isHidden = false | |
if self.autoCompletePlacesData.count < self.maxAutoCompleteDataCount{ | |
self.autoCompleteTableHeight = self.tableView?.contentSize.height | |
}else{ | |
self.autoCompleteTableHeight = 200 | |
} | |
self.tableView?.reloadData() | |
}else{ | |
self.tableView?.isHidden = true | |
} | |
} | |
}) | |
} | |
func GetPlaceDataByPlaceID(pPlaceID: String) { | |
//pPlaceID = "ChIJXbmAjccVrjsRlf31U1ZGpDM" | |
self.placesClient.lookUpPlaceID(pPlaceID, callback: { (place, error) -> Void in | |
if let error = error { | |
print("lookup place id query error: \(error.localizedDescription)") | |
return | |
} | |
if let place = place { | |
DispatchQueue.main.async { | |
self.tableView?.isHidden = self.hidesWhenSelected | |
self.selectedPlace?(place) | |
} | |
} | |
else { | |
print("No place details for \(pPlaceID)") | |
} | |
}) | |
} | |
} | |
//MARK: - UITableViewDataSource & UITableViewDelegate | |
extension AGAutocompletePlaceTextfield: UITableViewDataSource, UITableViewDelegate { | |
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { | |
return autoCompletePlacesData.count | |
} | |
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 highLightTypeTextedEnabled!{ | |
cell?.textLabel?.attributedText = showHighlightedTypedText(aStr: autoCompletePlacesData[indexPath.row].name as NSString) | |
}else{ | |
cell?.textLabel?.font = autoCompleteTextFont | |
cell?.textLabel?.textColor = autoCompleteTextColor | |
cell?.textLabel?.text = autoCompletePlacesData[indexPath.row].name | |
} | |
cell?.textLabel?.numberOfLines = 0 | |
cell?.contentView.gestureRecognizers = nil | |
return cell! | |
} | |
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { | |
let cell = tableView.cellForRow(at: indexPath) | |
if let selectedText = cell?.textLabel?.text { | |
self.GetPlaceDataByPlaceID(pPlaceID: autoCompletePlacesData[indexPath.row].id) | |
self.text = selectedText | |
self.resignFirstResponder() | |
} | |
} | |
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