var searchCountry = [String]() //to set up table
var searching = false
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
//table
extension DictionaryViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searching {
return searchCountry.count
} else {
return countrynameArr.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
if searching {
cell.textLabel?.text = searchCountry[indexPath.row]
} else {
cell.textLabel?.text = countrynameArr[indexPath.row]
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
myIndex = indexPath.row
performSegue(withIdentifier: "segue0", sender: self)
}
}
//Searchbar
extension DictionaryViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
searchCountry = countrynameArr.filter({$0.lowercased().prefix(searchText.count) == searchText.lowercased()})
searching = true
tblView.reloadData()
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searching = false
searchBar.text = " "
tblView.reloadData()
}
}
Hello everyone, it is the first time I do programming so my codes are probably terrible :). I created a tableview and a searchbar. When I scroll down the tableview and click on a item I get the right cell. When I use the searchbar I will get a filtered list (that is perfect) but then..when I click on the third item of the filtered list I get the third item form the whole list. Can you please tell me my mistake?
Related
So I have recently coded a search bar programmatically using tableview and navigation control. I am now having trouble finding information on how to segue the search bar after the user clicks on an item in the search bar.
I have tried using a view controller but that has not worked. I think my best bet is to do it programmatically.
EDIT There is no function yet in this code to implement a display screen. Im wondering what code is needed (im a very new developer) to get to another screen after clicking an element in the search bar. anything helps!!***
import UIKit
class SearchTableViewController: UITableViewController, UISearchBarDelegate {
let searchBar = UISearchBar()
let tableData = ["Boston University", "Boston College", "Northeastern University", "Suffolk University", "American University", "Harvard University", "Massachusetts Institute of Technology", "Tufts University", "Berklee College of Music", "Emerson College"]
//variables added for search function
var filteredArray = [String()]
var shouldShowSearchResults = false
override func viewDidLoad() {
super.viewDidLoad()
createSearcherBar()
}
func createSearcherBar() {
searchBar.showsBookmarkButton = false
searchBar.placeholder = "Search College"
searchBar.delegate = self
self.navigationItem.titleView = searchBar
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
filteredArray = tableData.filter({ (names: String) -> Bool in
return names.range(of: searchText, options: .caseInsensitive) != nil
})
if searchText != "" {
shouldShowSearchResults = true
self.tableView.reloadData()
}
else{
shouldShowSearchResults = false
self.tableView.reloadData()
}
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
if shouldShowSearchResults {
return filteredArray.count
}
else{
return tableData.count
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "basicCell", for: indexPath)
if shouldShowSearchResults {
cell.textLabel!.text = filteredArray[indexPath.row]
return cell
}
else{
cell.textLabel!.text = tableData[indexPath.row]
return cell
}
}
override func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
searchBar.endEditing(true)
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
shouldShowSearchResults = true
searchBar.endEditing(true)
self.tableView.reloadData()
}
/*
// Override to support conditional editing of the table view.
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
*/
/*
// Override to support editing the table view.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// Delete the row from the data source
tableView.deleteRows(at: [indexPath], with: .fade)
} else if editingStyle == .insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {
}
*/
/*
// Override to support conditional rearranging of the table view.
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the item to be re-orderable.
return true
}
*/
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}
Hopefully I can get a user to click on one of the options in a search bar, and it brings them to a different page.
First of all your method textDidChange is unnecessarily expensive because you are always filtering the array even if the search text is empty. This is more efficient
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchText.isEmpty {
shouldShowSearchResults = false
filteredArray.removeAll() // good practice to release memory when the search is finished
} else {
filteredArray = tableData.filter{ $0.range(of: searchText, options: .caseInsensitive) != nil }
shouldShowSearchResults = true
}
self.tableView.reloadData()
}
Second of all the declaration of filteredArray is slightly wrong. The parentheses must be outside the brackets. Your syntax declares a string array containing one empty string.
var filteredArray = [String]()
To answer your question implement didSelectRowAt and add the same shouldShowSearchResults logic to distinguish the arrays. Call performSegue and pass the string as sender
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let item = shouldShowSearchResults ? filteredArray[indexPath.row] : tableData[indexPath.row]
performSegue(withIdentifier: "MyIdentifier", sender: item)
}
and get it in prepare(for segue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard segue.identifier == "MyIdentifier" else { return }
let destinationController = segue.destination as! MyViewController
let item = sender as! String
...
I'd like to make it so that I can reorder a section in the app and also delete the section by swiping left on the section header (not on the row). Currently I have managed to delete and reorder the row however this is not what I want. I have looked into the documentation on the Apple website and noticed that there is a function (move section) and (delete section), however I do not know how to implement these functions in my code and can find no examples on the Internet on how to do so.
Any help would be greatly appreciated. Many thanks!
Swipe left to delete.
"Edit"
import UIKit
class TableViewController: UITableViewController {
var data = [String]()
#IBOutlet weak var editButton: UIBarButtonItem!
#IBAction func editAction(_ sender: UIBarButtonItem) {
self.tableView.isEditing = !self.tableView.isEditing
switch tableView.isEditing {
case true:
editButton.title = "Done"
default:
editButton.title = "Edit"
}
}
override func viewDidLoad() {
super.viewDidLoad()
data = ["Phone", "Pen", "Pencil"]
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return data.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 1
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell2", for: indexPath)
return cell
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1") as! TitleTableViewCell
cell.titleLabel.text = data[section]
return cell
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == UITableViewCellEditingStyle.delete
{
data.remove(at: indexPath.row)
tableView.reloadData()
}
}
override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
let movedObjTemp = data[sourceIndexPath.row]
data.remove(at: sourceIndexPath.row)
data.insert(movedObjTemp, at: destinationIndexPath.row)
}
}
I am having a weird issue where for some reason my UITableView is not being reloading after performing a search. The console prints out the correctly filtered data, but the table simply doesn't change. I have never encountered this issue, so I first attempted the solutions which naturally came to mind:
Tried tableView.reloadData() in the Main Queue
Quit Xcode, clean build, reinstall
Cleared out the derived data dir
I have found several similar issue in SO, but all of the solutions I've seen are things I've tried, mainly reloading tableview in main queue.
Hoping maybe I just simply have an issue in my code or something I'm missing.
I am running Xcode 8.3.3
import UIKit
class CategoriesViewController: UIViewController {
var isFiltering = false
var location = Location()
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var searchBar: UISearchBar!
var categoriesSearchResults = [Category]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.allowsSelection = true
tableView.keyboardDismissMode = .onDrag
let nib = UINib(nibName: "CategoryTableViewCell", bundle: nil)
self.tableView.register(nib, forCellReuseIdentifier:"CategoryTableViewCell");
searchBar.returnKeyType = UIReturnKeyType.done
searchBar.autocapitalizationType = .none
searchBar.delegate = self
}
extension CategoriesViewController : UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 60
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("HI")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isFiltering {
return self.categoriesSearchResults.count
}
return self.location.categories.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
if let cell = self.tableView.dequeueReusableCell(withIdentifier: "CategoryTableViewCell", for: indexPath) as? CategoryTableViewCell {
var category: Category
if isFiltering {
category = self.categoriesSearchResults[indexPath.row]
} else {
category = self.location.categories[indexPath.row]
}
cell.name.text = category.name
cell.status.textColor = UIColor.lightGray
cell.status.text = "Not Verified"
}
return cell
}
}
extension CategoriesViewController : UISearchBarDelegate {
func searchBarIsEmpty() -> Bool{
return self.searchBar.text?.isEmpty ?? true
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
self.isFiltering = true
self.categoriesSearchResults.removeAll()
tableView.reloadData()
self.view.endEditing(true)
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchBarIsEmpty() {
self.view.endEditing(true)
self.isFiltering = false
} else {
self.isFiltering = true
self.categoriesSearchResults = self.location.categories.filter({ (category: Category) -> Bool in
return category.name.lowercased().contains(searchText.lowercased())
})
}
tableView.reloadData()
}
}
and my custom table view cell:
import UIKit
class CategoryTableViewCell: UITableViewCell {
#IBOutlet weak var name: UILabel!
#IBOutlet weak var status: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
override func prepareForReuse() {
super.prepareForReuse()
self.name.text = ""
self.status.text = ""
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Thank you in advance.
EDIT: Might also be worth mentioning, when I am actively searching, the function tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) is not called??
The scope of if let nests in its scope. In your code you are always returning let cell = UITableViewCell(). Try returning it inside the if let :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
if let cell = self.tableView.dequeueReusableCell(withIdentifier: "CategoryTableViewCell", for: indexPath) as? CategoryTableViewCell {
var category: Category
if isFiltering {
category = self.categoriesSearchResults[indexPath.row]
} else {
category = self.location.categories[indexPath.row]
}
cell.name.text = category.name
cell.status.textColor = UIColor.lightGray
cell.status.text = "Not Verified"
/// RETURN CELL HERE
return cell
}
return cell
}
I am interested in using a tableView to list possible addresses based on inputs in the search bar. After selecting the cell that contains the address desired, the search bar text consists of the address, however I want the possible addresses (cells) to disappear. Does self.searchResultsTableView.reloadData() in didSelectRowAt clear all the cells or is there another command? I am not certain how to clear the cells after selecting the appropriate address without iterating and having the suggestion introduce more cells.
import UIKit
import MapKit
class SearchViewController: UIViewController {
#IBOutlet weak var searchBar: UISearchBar!
var searchCompleter = MKLocalSearchCompleter()
var searchResults = [MKLocalSearchCompletion]()
var searchSource: [String]?
#IBOutlet weak var searchResultsTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
searchCompleter.delegate = self
searchBar.delegate = self
}
}
extension SearchViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
searchCompleter.queryFragment = searchText
}
}
extension SearchViewController: MKLocalSearchCompleterDelegate {
func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
searchResults = completer.results
searchResultsTableView.reloadData()
}
func completer(_ completer: MKLocalSearchCompleter, didFailWithError error: Error) {
// handle error
}
}
extension SearchViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return searchResults.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let searchResult = searchResults[indexPath.row]
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: nil)
cell.textLabel?.text = searchResult.title
cell.detailTextLabel?.text = searchResult.subtitle
return cell
}
}
extension SearchViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let completion = searchResults[indexPath.row]
let searchRequest = MKLocalSearchRequest(completion: completion)
let search = MKLocalSearch(request: searchRequest)
search.start { (response, error) in
let coordinate = response?.mapItems[0].placemark.coordinate
print(String(describing: coordinate))
print(response?.mapItems)
self.searchBar.text = response?.mapItems[0].name
}
self.searchResultsTableView.reloadData()
}
}
If you want to clear your tableView then you need to make your datasource array empty and then reload the tableView.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let completion = searchResults[indexPath.row]
let searchRequest = MKLocalSearchRequest(completion: completion)
let search = MKLocalSearch(request: searchRequest)
search.start { (response, error) in
let coordinate = response?.mapItems[0].placemark.coordinate
print(String(describing: coordinate))
print(response?.mapItems)
self.searchBar.text = response?.mapItems[0].name
}
//Make empty your array ant then reload tableView
searchResults = []
self.searchResultsTableView.reloadData()
}
I want to make tableview cell have dynamic label or image in it. I don't want to loop add subview to content view of tableview cell because it will run slow when you scroll. So can anyone help me please? Like this image:
My code is;
Without knowing the context of your code, something like this can work:
NOTE This will work on iOS <= 8
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
#IBOutlet weak var TableVC: UITableView!
let elements = ["item1 item2 item3","item1 item2", "item1"]
override func viewDidLoad() {
super.viewDidLoad()
self.TableVC.delegate = self
self.TableVC.dataSource = self
self.TableVC.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return elements.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.numberOfLines = 0
cell.textLabel?.attributedText = addTextToAttribute(text: elements[indexPath.item])
return cell
}
func addTextToAttribute(text : String) -> NSMutableAttributedString {
let mutString = NSMutableAttributedString()
let s = text.components(separatedBy: .whitespaces)
let attr: [String: AnyObject] = [NSFontAttributeName: UIFont.boldSystemFont(ofSize: 20)]
for text in s {
mutString.append(NSAttributedString(string: "-" + text + "\n", attributes: attr))
}
return mutString
}
}