I am trying to implement autocompletion, but can't find an example that works in Swift. Below, I'm tring to convert Ray Wenderlich's autocompletion tutorial and example code from 2010. Finally, the code compiles, but the table containing possible completions does not appear, and I don't have the experience to see why it is not unhidden by shouldChangeCharactersInRange.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate {
#IBOutlet weak var textField: UITextField!
let autocompleteTableView = UITableView(frame: CGRectMake(0,80,320,120), style: UITableViewStyle.Plain)
var pastUrls = ["Men", "Women", "Cats", "Dogs", "Children"]
var autocompleteUrls = [String]()
override func viewDidLoad() {
super.viewDidLoad()
autocompleteTableView.delegate = self
autocompleteTableView.dataSource = self
autocompleteTableView.scrollEnabled = true
autocompleteTableView.hidden = true
}
func textField(textField: UITextField!, shouldChangeCharactersInRange range: NSRange, replacementString string: String!) -> Bool
{
autocompleteTableView.hidden = false
var substring = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
searchAutocompleteEntriesWithSubstring(substring)
return true // not sure about this - could be false
}
func searchAutocompleteEntriesWithSubstring(substring: String)
{
autocompleteUrls.removeAll(keepCapacity: false)
var indexOfPastUrls = 0
for curString in pastUrls
{
let substringRange = curString.rangeOfString(curString)
if (indexOfPastUrls == 0)
{
autocompleteUrls.append(curString)
}
indexOfPastUrls = indexOfPastUrls + 1
}
autocompleteTableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return autocompleteUrls.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let autoCompleteRowIdentifier = "AutoCompleteRowIdentifier"
var cell : UITableViewCell = tableView.dequeueReusableCellWithIdentifier(autoCompleteRowIdentifier, forIndexPath: indexPath) as UITableViewCell
let index = indexPath.row as Int
cell.textLabel.text = autocompleteUrls[index]
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let selectedCell : UITableViewCell = tableView.cellForRowAtIndexPath(indexPath)!
textField.text = selectedCell.textLabel.text
}
}
Replace your searchAutocompleteEntriesWithSubstring function content with the one below. I hope it would help you.
func searchAutocompleteEntriesWithSubstring(substring: String)
{
autocompleteUrls.removeAll(keepCapacity: false)
for curString in pastUrls
{
var myString:NSString! = curString as NSString
var substringRange :NSRange! = myString.rangeOfString(substring)
if (substringRange.location == 0)
{
autocompleteUrls.append(curString)
}
}
autocompleteTableView.reloadData()
}
The table was not appearing because the UITextField delegate was not to self in viewDidLoad. There was another final issue with the table not showing the autocompletion results, but this is also fixed. Ray Wenderlich's basic Objective-C autocompletion tutorial converted to Swift:
class ViewController: UIViewController, UITextFieldDelegate, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var textField: UITextField!
#IBOutlet var autocompleteTableView: UITableView!
// #IBOutlet weak var autocompleteTableView = UITableView(frame: CGRectMake(0,80,320,120), style: UITableViewStyle.Plain)
var pastUrls = ["Men", "Women", "Cats", "Dogs", "Children"]
var autocompleteUrls = [String]()
override func viewDidLoad() {
super.viewDidLoad()
textField.delegate = self
autocompleteTableView!.delegate = self
autocompleteTableView!.dataSource = self
autocompleteTableView!.scrollEnabled = true
autocompleteTableView!.hidden = true
}
func textField(textField: UITextField!, shouldChangeCharactersInRange range: NSRange, replacementString string: String!) -> Bool
{
println("banana")
autocompleteTableView!.hidden = false
var substring = (self.textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
searchAutocompleteEntriesWithSubstring(substring)
return true
}
func searchAutocompleteEntriesWithSubstring(substring: String)
{
autocompleteUrls.removeAll(keepCapacity: false)
println(substring)
for curString in pastUrls
{
println(curString)
var myString: NSString! = curString as NSString
var substringRange: NSRange! = myString.rangeOfString(substring)
if (substringRange.location == 0)
{
autocompleteUrls.append(curString)
}
}
autocompleteTableView!.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return autocompleteUrls.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let autoCompleteRowIdentifier = "AutoCompleteRowIdentifier"
var cell = tableView.dequeueReusableCellWithIdentifier(autoCompleteRowIdentifier) as? UITableViewCell
if let tempo1 = cell
{
let index = indexPath.row as Int
cell!.textLabel.text = autocompleteUrls[index]
} else
{
cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: autoCompleteRowIdentifier)
}
return cell!
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let selectedCell : UITableViewCell = tableView.cellForRowAtIndexPath(indexPath)!
textField.text = selectedCell.textLabel.text
}
}
I put together a tutorial that is full of pictures on how to recreate this now 6 year old tutorial
matthewhsingleton.com/coding-with-a-rubber-ducky/2016/5/26/… – RubberDucky4444
For future guys, that might get to work on autocomplete texfield with Swift 2, the code provided by #dane works well. but you have to change this line:
let cell : UITableViewCell = tableView.dequeueReusableCellWithIdentifier(autoCompleteRowIdentifier, forIndexPath: indexPath) as UITableViewCell
by
let cell = UITableViewCell(style: UITableViewCellStyle.Default , reuseIdentifier: cellIdentifier)
Also, you might notice that the it is case sensitive, and doesn't work if you enter lowercase string (e.g cats) by default. So to solve this issue you can replace add the option "CaseSensitiveSearch" to the substringRange declaration (in the func searchAutocompleteEntriesWithSubstring). it should look like:
let substringRange :NSRange! = myString.rangeOfString(substring,options [.CaseInsensitiveSearch])
Hope it will help you save one day!!!
Fixed for iOS 9.0 and Swift 2:
import UIKit
class UIAutoCompleteTextField: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate {
#IBOutlet weak
var textField: UITextField!
let autocompleteTableView = UITableView(frame: CGRectMake(0, 80, 320, 120), style: UITableViewStyle.Plain)
var pastUrls = ["Men", "Women", "Cats", "Dogs", "Children"]
var autocompleteUrls = [String]()
override func viewDidLoad() {
super.viewDidLoad()
autocompleteTableView.delegate = self
autocompleteTableView.dataSource = self
autocompleteTableView.scrollEnabled = true
autocompleteTableView.hidden = true
}
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) - > Bool {
autocompleteTableView.hidden = false
let substring = (textField.text!as NSString).stringByReplacingCharactersInRange(range, withString: string)
searchAutocompleteEntriesWithSubstring(substring)
return true // not sure about this - could be false
}
func searchAutocompleteEntriesWithSubstring(substring: String) {
autocompleteUrls.removeAll(keepCapacity: false)
for curString in pastUrls {
let myString: NSString! = curString as NSString
let substringRange: NSRange! = myString.rangeOfString(substring)
if (substringRange.location == 0) {
autocompleteUrls.append(curString)
}
}
autocompleteTableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) - > Int {
return autocompleteUrls.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) - > UITableViewCell {
let autoCompleteRowIdentifier = "AutoCompleteRowIdentifier"
let cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier(autoCompleteRowIdentifier, forIndexPath: indexPath) as UITableViewCell
let index = indexPath.row as Int
cell.textLabel!.text = autocompleteUrls[index]
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let selectedCell: UITableViewCell = tableView.cellForRowAtIndexPath(indexPath) !
textField.text = selectedCell.textLabel!.text
}
}
Here's a way to add multiple tags based on "#" being typed in like twitter.
Variable typedSubstring is the global substring.
func textField(textField: UITextField!, shouldChangeCharactersInRange range: NSRange, replacementString string: String!) -> Bool {
autocompleteTableView!.hidden = false
var changedText = (self.textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
var items = changedText.componentsSeparatedByString("#")
if (items.count > 0) {
typedSubstring = "#" + items.lastObject as NSString
self.searchAutocompleteEntriesWithSubstring(typedSubstring)
}
return true
}
Improved on DrWhat's solution so that when you select a cell, it appends it correctly after where the user has already typed in.
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
let selectedCell : UITableViewCell = tableView.cellForRowAtIndexPath(indexPath)!
let selectedText = selectedCell.textLabel?.text as String!
// Remove what has been typed already
let trimmedString = selectedText.stringByReplacingOccurrencesOfString(typedSubstring, withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
var currentTextField = textField.text
// then append to what has been typed
textField.text = currentTextField + trimmedString
Got it working with the below. The upper/lower case threw it off initially. I'm using it to autocomplete country names...
import UIKit
class billingAddressViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate {
#IBOutlet var countryTextField: UITextField!
#IBOutlet var countryTableView: UITableView!
var autocompleteCountries = [String]()
// Get list of countries
let countries = NSLocale.ISOCountryCodes().map { (code:String) -> String in
let id = NSLocale.localeIdentifierFromComponents([NSLocaleCountryCode: code])
return NSLocale(localeIdentifier: "en_US").displayNameForKey(NSLocaleIdentifier, value: id) ?? "Country not found for code: \(code)"
}
override func viewDidLoad() {
super.viewDidLoad()
countryTextField.delegate = self
countryTableView!.delegate = self
countryTableView!.dataSource = self
countryTableView!.scrollEnabled = true
countryTableView!.hidden = true
}
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
print("text field has changed")
countryTableView!.hidden = false
let substring = (self.countryTextField.text! as NSString).stringByReplacingCharactersInRange(range, withString: string)
print(substring)
searchAutocompleteEntriesWithSubstring(substring)
return true
}
func searchAutocompleteEntriesWithSubstring(substring: String) {
autocompleteCountries.removeAll(keepCapacity: false)
print(substring)
for curString in countries {
//print(curString)
let myString: NSString! = curString.lowercaseString as NSString
let substringRange: NSRange! = myString.rangeOfString(substring.lowercaseString)
if (substringRange.location == 0) {
autocompleteCountries.append(curString)
}
}
countryTableView!.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return autocompleteCountries.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let autoCompleteRowIdentifier = "AutoCompleteRowIdentifier"
var cell = tableView.dequeueReusableCellWithIdentifier(autoCompleteRowIdentifier) as UITableViewCell!
if let tempo1 = cell {
let index = indexPath.row as Int
cell!.textLabel!.text = autocompleteCountries[index]
}
else {
cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: autoCompleteRowIdentifier)
}
return cell!
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let selectedCell : UITableViewCell = tableView.cellForRowAtIndexPath(indexPath)!
countryTextField.text = selectedCell.textLabel!.text
countryTableView.hidden = true
}
}
table view added without storyboard
class ViewController: UIViewController , UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate {
#IBOutlet weak var textField: UITextField!
var autocompleteTableView: UITableView!
var pastUrls = ["Men", "Women", "Cats", "Dogs", "Children","aaaaaaaaa","aaaaaaaaaaaaaaaaaaa","aaaaaaaaa","a","aa","aaa"]
var autocompleteUrls = [String]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
autocompleteTableView = UITableView(frame: CGRectMake(self.textField.bounds.minX,self.textField.bounds.maxY,self.textField.bounds.width,self.textField.bounds.height * 4), style: UITableViewStyle.Plain)
textField.delegate = self
autocompleteTableView.delegate = self
autocompleteTableView.dataSource = self
autocompleteTableView.scrollEnabled = true
autocompleteTableView.hidden = false
autocompleteTableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
self.view.addSubview(autocompleteTableView)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func textField(textField: UITextField!, shouldChangeCharactersInRange range: NSRange, replacementString string: String!) -> Bool
{
autocompleteTableView.hidden = false
var substring = (self.textField.text! as NSString).stringByReplacingCharactersInRange(range, withString: string)
searchAutocompleteEntriesWithSubstring(substring)
return true // not sure about this - could be false
}
func searchAutocompleteEntriesWithSubstring(substring: String)
{
autocompleteUrls.removeAll(keepCapacity: false)
for curString in pastUrls
{
var myString:NSString! = curString as NSString
var substringRange :NSRange! = myString.rangeOfString(substring)
if (substringRange.location == 0)
{
autocompleteUrls.append(curString)
}
}
autocompleteTableView.reloadData()
//autocompleteTableView.hidden = false
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return autocompleteUrls.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let autoCompleteRowIdentifier = "cell"
var cell : UITableViewCell = tableView.dequeueReusableCellWithIdentifier(autoCompleteRowIdentifier, forIndexPath: indexPath) as UITableViewCell
let index = indexPath.row as Int
cell.textLabel!.text = autocompleteUrls[index]
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let selectedCell : UITableViewCell = tableView.cellForRowAtIndexPath(indexPath)!
textField.text = self.autocompleteUrls[indexPath.row]
self.autocompleteTableView.hidden = true
}
}
This post helped me thanks, just in case you guys are working with google places API in swift 3 and you need case-insensitive here is the updated code you just have to put:
let subStringRange : NSRange! = myString.range(of: substring, options: .caseInsensitive)
Replace cellForRowAtIndexPath with following function
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = UITableViewCell()
var data = autocompleteUrls[indexPath.row]
cell.textLabel?.text = data as String
return cell
}
Here you have a nice-to-have library in order to simplify that work: https://github.com/apasccon/SearchTextField
Add view.addSubview(autocompleteTableView)
in your viewdidload. It will work.
Related
I am using api. I post some data and store response in tableview. I put searchbar for search data from tableview. I want when I write 3 words in searchbar after that my api will call and data show in the tableview. I am using uitextfield for searchbar.
var array = [String]()
var tabledata = [String]()
var tableFilterData = [String]()
var isSearch :Bool!
let cellReuseIdentifier = "cell"
#IBOutlet weak var searchTextfield: UITextField!
#IBOutlet weak var tableview: UITableView!
#IBOutlet weak var heightConstraint: NSLayoutConstraint!
#IBAction func textfieldchanged(_ sender: Any) {
tableview.isHidden = true
}
override func viewDidLoad() {
super.viewDidLoad()
isSearch = false
self.apicalling()
searchTextfield.addTarget(self, action: #selector(textFieldActive), for: UIControlEvents.touchDown)
}
public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool{
var searchText = textField.text! + string
if string == "" {
searchText = (searchText as String).substring(to: searchText.index(before: searchText.endIndex))
}
if searchText == "" {
isSearch = false
tableview.reloadData()
}
else{
getSearchArrayContains(searchText)
}
return true
}
func getSearchArrayContains(_ text : String) {
var predicate : NSPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", text)
tableFilterData = (tabledata as NSArray).filtered(using: predicate) as! [String]
isSearch = true
tableview.reloadData()
}
override func viewDidLayoutSubviews()
{
heightConstraint.constant = tableview.contentSize.height
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
{
guard let touch:UITouch = touches.first else
{
return;
}
if touch.view != tableview
{
searchTextfield.endEditing(true)
tableview.isHidden = true
}
}
#objc func textFieldActive() {
tableview.isHidden = !tableview.isHidden
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
my tableview part is this
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isSearch! {
return tableFilterData.count
}
else{
return tabledata.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell") as UITableViewCell!
// Set text from the data model
if isSearch! {
cell.textLabel?.text = tableFilterData[indexPath.row]
cell.textLabel?.font = searchTextfield.font
}
else{
cell.textLabel?.text = tabledata[indexPath.row]
cell.textLabel?.font = searchTextfield.font
}
return cell
}
In my api calling my parameter is this
let param = ["var_name": "sha","API":"user_search","auth_token":authToken!]
but I want searchdata pass into the "var_name" .
my searchbar is working properly but I want when I write 3 words in searchbar after that api will call and that three data pass into the api as a parameters in "var_name".
I have a popup with searchBar at the top and TableView below it. TableView is populated by dynamic data. I have a custom tableViewCell, with a label for names and a checkBox(M13CheckBox Library) to select a name.
Now, when I search for a name, Firstly the tableView is not loaded as the user types a name in the search bar. For eg, Suppose there are persons named "Mary", "Mackenzie", "Margaret" and "Mallory". I want to search for "Margaret", so as I start typing "Mar" in searchBar, then "Mary" and "Margaret" are filtered properly in tableView, but when I go back i.e "Ma", then it should show all the 4 names, since "Ma" is present in the list, But the tableView does not show anything.
So tableView should always reload as user types in searchBar if the letters are contained in the names. Please help me sort this issue. Since it is a popup I am passing data to tableView from another VC, by notification.
Here is my code for search VC:
class ParticipantsListVC: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate{
public static var participantNameArray:[String] = [String]() //global var
var viewController: ViewController!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
searchParticipantFilter.delegate = self
viewController = ViewController()
let notificationName = NSNotification.Name("reloadList")
NotificationCenter.default.addObserver(forName: notificationName, object: nil, queue: OperationQueue.main) { (notifObject) in
self.tableView.reloadData()
}
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText:String) {
if searchText == "" {
ParticipantsListVC.participantNameArray.removeAll()
viewController.getParticipantList() // func to get list from sever
}else{
ParticipantsListVC.participantNameArray = ParticipantsListVC.participantNameArray.filter({(name) -> Bool in
return name.lowercased().contains(searchText.lowercased())
})
}
self.tableView.reloadData()
}
}
Also if I select a name, then checkBox is selected in front of that name.But when I click on cancel(X) in searchBar, then always the first cell in tableView is shown selected and not the name that I had selected. I don't know why always the first cell gets selected, after selecting name from filtered list.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ParticipantListCell
let dict = ParticipantsListVC.participantNameArray[indexPath.row]
cell.participantNameLabel.text = dict
if selectedIndexPaths.contains(indexPath) {
cell.selectedParticipantCB.setCheckState(.checked, animated: true)
}else{
cell.selectedParticipantCB.setCheckState(.unchecked, animated: true)
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Since any random cell was getting selected on scrolling So I added this code.
tableView.deselectRow(at: indexPath, animated: true)
if selectedIndexPaths.contains(indexPath) {
selectedIndexPaths.removeObject(object: indexPath)
}else{
selectedIndexPaths.append(indexPath)
}
tableView.reloadData()
}
I don't want to use searchBar in headerView or another tableView to show filtered list. Please much appreciated.Thank you.
You need to create another array to hold the backup of data array.
var arrParticipantList = [String]()
var arrParticipantListBackup = [String]()
override func viewDidLoad() {
super.viewDidLoad()
self.searchBar.delegate = self
self.tblParticipantList.delegate = self
self.tblParticipantList.dataSource = self
self.arrParticipantList = ["Mary", "Mackenzie", "Margaret", "Mallory","Molly"]
self.arrParticipantListBackup = self.arrParticipantList
}
Code to search for search string, refill array and reload tableview
extension ViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
var searchText = searchBar.text! + text
if range.length > 0 {
if range.location == 0 {
self.arrParticipantList = self.arrParticipantListBackup
self.tblParticipantList.reloadData()
return true
}
searchText = String(searchText.dropLast(range.length))
}
self.arrParticipantList = self.arrParticipantListBackup.filter({$0.lowercased().hasPrefix(searchText.lowercased())})
self.tblParticipantList.reloadData()
return true
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
self.searchBar.text = ""
self.searchBar.resignFirstResponder()
self.arrParticipantList = self.arrParticipantListBackup
self.tblParticipantList.reloadData()
}
}
Code for tableview
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.arrParticipantList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
cell?.textLabel?.text = self.arrParticipantList[indexPath.row]
return cell!
}
}
Hope this solves your issue.
struct namelist {
var searchname: NSString
}
var searchActive = Bool()
var newSearchArray = [namelist]()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return searchActive ? newSearchArray.count : nameOldArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let Cell:SearchTableViewCell! = tableView.dequeueReusableCell(withIdentifier: "Cell") as! SearchTableViewCell
Cell.selectionStyle = .none
if (searchActive == true) {
if ( newSearchArray.count > 0) {
var para = NSMutableAttributedString()
para = NSMutableAttributedString(string:(newSearchArray[indexPath.row].searchname) as String)
do {
let regex = try NSRegularExpression(pattern: searchText, options: NSRegularExpression.Options.caseInsensitive )
let nsstr = newSearchArray[indexPath.row].searchname
text = nsstr as String
let all = NSRange(location: 0, length: nsstr.length)
var matches : [String] = [String]()
regex.enumerateMatches(in: text, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: all) {
(result : NSTextCheckingResult?, _, _) in
if let r = result {
let results = nsstr.substring(with: r.range) as String
matches.append(results)
let substringrange = result!.rangeAt(0)
para.addAttribute(NSForegroundColorAttributeName, value:UIColor.init(red: 237/255.0, green: 60/255.0, blue: 58/255.0, alpha: 1.0), range: substringrange)
Cell.namelbl.attributedText = para
}
}
} catch {
}
}
}
else {
Cell.namelbl.text = self.searchname[indexPath.row] as? String
}
return Cell
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
searchArray.removeAllObjects()
newSearchArray.removeAll()
if Search.text != nil {
for i in 0 ..< searchname.count {
searchText = Search.text!
text = ((searchname.object(at: i))) as! String
if text.lowercased().contains(searchText.lowercased()) {
let elm = namelist(searchname: text as NSString)
self.newSearchArray.append(elm)
}
}
}
searchActive = !newSearchArray.isEmpty
searchBar.resignFirstResponder()
yourTableName.reloadData()
}
I am working on a swift project involving delegates and protocols. I have two files that are working smoothly in this, however I am having an issue where the delegate keeps coming up nil in a different two files where I am doing the same thing as before with different names. It seems like the delegate isn't being set/created for some reason. See code below:
ItemDetailViewController
import UIKit
protocol ItemDetailViewControllerDelegate: class {
func itemDetailViewControllerDidCancel(controller: ItemDetailViewController)
func itemDetailViewController(controller: ItemDetailViewController, didFinishAddingItem item: ChecklistItem)
func itemDetailViewController(controller: ItemDetailViewController, didFinishEditingItem item: ChecklistItem)
}
class ItemDetailViewController: UITableViewController, UITextFieldDelegate {
**weak var delegate: ItemDetailViewControllerDelegate?**
#IBOutlet weak var doneBarButton: UIBarButtonItem!
#IBOutlet weak var textField: UITextField!
var itemToEdit: ChecklistItem?
#IBAction func cancel() {
delegate?.itemDetailViewControllerDidCancel(self)
}
#IBAction func done() {
if let item = itemToEdit {
item.text = textField.text!
delegate?.itemDetailViewController(self, didFinishEditingItem: item)
}
else {
let item = ChecklistItem()
item.text = textField.text!
delegate?.itemDetailViewController(self, didFinishAddingItem: item)
}
}
override func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
return nil
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
textField.becomeFirstResponder()
}
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let oldText: NSString = textField.text!
let newText: NSString = oldText.stringByReplacingCharactersInRange(range, withString: string)
doneBarButton.enabled = (newText.length > 0)
return true
}
override func viewDidLoad() {
super.viewDidLoad()
textField.delegate = self
if let item = itemToEdit {
title = "Edit Item"
textField.text = item.text
doneBarButton.enabled = true
}
}
}
ChecklistViewController
import UIKit
class ChecklistViewController: UITableViewController, ItemDetailViewControllerDelegate {
var checklist: Checklist!
override func viewDidLoad() {
super.viewDidLoad()
title = checklist.name
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return checklist.items.count
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let item = checklist.items[indexPath.row]
performSegueWithIdentifier("ShowMap", sender: item)
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("ChecklistItem", forIndexPath: indexPath)
let item = checklist.items[indexPath.row]
configureTextForCell(cell, withChecklistItem: item)
return cell
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
// 1
checklist.items.removeAtIndex(indexPath.row)
// 2
let indexPaths = [indexPath]
tableView.deleteRowsAtIndexPaths(indexPaths, withRowAnimation: .Automatic)
}
func configureTextForCell(cell: UITableViewCell,withChecklistItem item: ChecklistItem) {
let label = cell.viewWithTag(1000) as! UILabel
label.text = item.text
}
func itemDetailViewControllerDidCancel(controller: ItemDetailViewController) {
dismissViewControllerAnimated(true, completion: nil)
}
func itemDetailViewController(controller: ItemDetailViewController,didFinishEditingItem item: ChecklistItem) {
if let index = checklist.items.indexOf(item) { // indexOf needs to compare (test for equality) item to the items in the array
let indexPath = NSIndexPath(forRow: index, inSection: 0)
if let cell = tableView.cellForRowAtIndexPath(indexPath) {
configureTextForCell(cell, withChecklistItem: item)
}
}
dismissViewControllerAnimated(true, completion: nil)
}
func itemDetailViewController(controller: ItemDetailViewController, didFinishAddingItem item: ChecklistItem) {
let newRowIndex = checklist.items.count
checklist.items.append(item)
let indexPath = NSIndexPath(forRow: newRowIndex, inSection: 0)
let indexPaths = [indexPath]
tableView.insertRowsAtIndexPaths(indexPaths, withRowAnimation: .Automatic)
dismissViewControllerAnimated(true, completion: nil)
}
override func prepareForSegue(segue: UIStoryboardSegue,sender: AnyObject?) {
if segue.identifier == "AddItem" {
let navigationController = segue.destinationViewController
as! UINavigationController
let controller = navigationController.topViewController
as! ItemDetailViewController
controller.delegate = self
}
else if segue.identifier == "EditItem" {
let navigationController = segue.destinationViewController
as! UINavigationController
let controller = navigationController.topViewController
as! ItemDetailViewController
controller.delegate = self
if let indexPath = tableView.indexPathForCell(
sender as! UITableViewCell) {
controller.itemToEdit = checklist.items[indexPath.row]
}
}
}
}
Is it possible that your segue identifiers in ChecklistViewController:prepareForSegue are not correct, or are not setup correctly in the storyboard? If so the destination controller delegate would not get set.
I have a text field that, when you type a name, should show a suggested name that get I from an array with JSON, the problem is that it shows only one name. For example if I type Tom it shows only Tom Cruise and not Tommy Gien. How can I resolve that?
CODE:
class ViewController: UIViewController, UITextViewDelegate, UITextFieldDelegate,UITableViewDataSource, UITableViewDelegate {
let save = NSUserDefaults.standardUserDefaults()
#IBOutlet var amountPoints: UILabel!
#IBOutlet var reasonView: UITextView!
#IBOutlet var toField: UITextField!
#IBOutlet var pointsField: UITextField!
#IBOutlet var autocompleteTableView: UITableView!
var pastUrls: [String] = []
var autocompleteUrls = [String]()
override func viewDidLoad()
{
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
toField.delegate = self
reasonView.layer.cornerRadius = 1
reasonView.layer.borderWidth = 0.7
reasonView.layer.borderColor = UIColor.grayColor().CGColor
autocompleteTableView.delegate = self
autocompleteTableView.dataSource = self
autocompleteTableView.scrollEnabled = true
autocompleteTableView.hidden = true
getallUser()
var Names = save.arrayForKey("give.Name")
pastUrls = Names as! [String]
print(pastUrls)
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
{
autocompleteTableView.hidden = false
let substring = (textField.text! as NSString).stringByReplacingCharactersInRange(range, withString: string)
searchAutocompleteEntriesWithSubstring(substring)
return true // not sure about this - could be false
}
func searchAutocompleteEntriesWithSubstring(substring: String)
{
autocompleteUrls.removeAll(keepCapacity: false)
for curString in pastUrls
{
let myString:NSString! = curString as NSString
let substringRange :NSRange! = myString.rangeOfString(substring)
if (substringRange.location == 0)
{
autocompleteUrls.append(curString)
}
}
autocompleteTableView.reloadData()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return autocompleteUrls.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let autoCompleteRowIdentifier = "AutoCompleteRowIdentifier"
let cell : UITableViewCell = tableView.dequeueReusableCellWithIdentifier(autoCompleteRowIdentifier, forIndexPath: indexPath) as UITableViewCell
let index = indexPath.row as Int
cell.textLabel!.text = autocompleteUrls[index]
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let selectedCell : UITableViewCell = tableView.cellForRowAtIndexPath(indexPath)!
toField.text = selectedCell.textLabel!.text
autocompleteTableView.hidden = true
}
func textViewDidBeginEditing(textView: UITextView) {
reasonView.text = ""
}
func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
if text == "\n"
{
textView.resignFirstResponder()
return false
}
return true
}
#IBAction func giveButton(sender: UIButton) {
}
#IBAction func returnButton(sender: UIBarButtonItem) {
self.dismissViewControllerAnimated(true, completion: nil)
}
Try replacing your method in seachAutocompleteEntriesWithSubtring with the following
func searchAutocompleteEntriesWithSubstring(substring: String)
{
autocompleteUrls.removeAll(keepCapacity: false)
for curString in pastUrls
{
var myString:NSString! = curString as NSString
var substringRange :NSRange! = myString.rangeOfString(substring)
if (substringRange.location == 0)
{
autocompleteUrls.append(curString)
}
}
autocompleteTableView.reloadData()
}
I am working on the auto complete textfield in Swift.
I used this example:
Getting autocomplete to work in swift
But for me cellForRowAtIndexPath method is not calling at all. I don't know what mistake I have done here.
My code is like this:
#IBOutlet weak var autocompleteTableView: UITableView!
#IBOutlet weak var searclLocationTextField: UITextField!
var pastUrls = ["Men", "Women", "Cats", "Dogs", "Children"]
var autocompleteUrls = [String]()
override func viewDidLoad() {
super.viewDidLoad()
autocompleteTableView.scrollEnabled = true
autocompleteTableView.hidden = true
// Do any additional setup after loading the view.
}
// func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
// {
// autocompleteTableView.hidden = false
// var substring = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
//
// searchAutocompleteEntriesWithSubstring(substring)
// return true // not sure about this - could be false
// }
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
{
println("banana")
autocompleteTableView.hidden = false
var substring = (searclLocationTextField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
searchAutocompleteEntriesWithSubstring(substring)
autocompleteTableView.reloadData()
return true
}
func searchAutocompleteEntriesWithSubstring(substring: String)
{
autocompleteUrls.removeAll(keepCapacity: false)
for curString in pastUrls
{
var myString:NSString! = curString as NSString
var substringRange :NSRange! = myString.rangeOfString(substring)
if (substringRange.location == 0)
{
autocompleteUrls.append(curString)
}
}
autocompleteTableView.reloadData()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return autocompleteUrls.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let autoCompleteRowIdentifier = "AutoCompleteRowIdentifier"
var cell = tableView.dequeueReusableCellWithIdentifier(autoCompleteRowIdentifier) as? UITableViewCell
if let tempo1 = cell
{
let index = indexPath.row as Int
cell!.textLabel!.text = autocompleteUrls[index]
} else
{
cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: autoCompleteRowIdentifier)
}
return cell!
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let selectedCell : UITableViewCell = tableView.cellForRowAtIndexPath(indexPath)!
searclLocationTextField.text = selectedCell.textLabel!.text
println(searclLocationTextField.text)
}
func searchAutocompleteEntriesWithSubstring(substring: String)
{
autocompleteUrls.removeAll(keepCapacity: false)
for curString in pastUrls
{
var myString:NSString! = curString as NSString
var substringRange :NSRange! = myString.rangeOfString(substring)
if (substringRange.location == 0)
{
autocompleteUrls.append(curString)
}
}
autocompleteTableView.reloadData()
}
you are getting finally array at this line autocompleteUrls.append(curString) after that you are reloading the tableView, so log the array count below autocompleteUrls.append(curString) this line and if you will get something definitely cellForRowAtIndexPath will be hit.