I have a UITableViewController (with 6 cells) and a UISearchController. Whenever I tap on the search bar, the table view cells disappear and are replaced with an empty tableView until I start typing something and the search results appear. How can I make it so that cells stay underneath when I tap the search bar?
Here is my code pertaining to the SearchController:
In viewDidLoad():
self.resultSearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self
controller.dimsBackgroundDuringPresentation = true
controller.searchBar.sizeToFit()
controller.searchBar.barTintColor = UIColor(red: 240/255, green: 240/255, blue: 240/255, alpha: 1)
self.tableView.tableHeaderView = controller.searchBar
self.definesPresentationContext = true
return controller
})()
The rest:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (self.resultSearchController.active) {
return self.filteredTableData.count
} else {
return self.items.count
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! TableViewCell
var item : TextCell
if (self.resultSearchController.active) {
item = filteredTableData[indexPath.row]
return cell
} else {
item = items[indexPath.row]
cell.userText.text = item.userText
cell.userPreferredName.text = item.userPreferredName
cell.userName.text = item.userName
cell.userImage.layer.cornerRadius = 23.75
cell.userImage.clipsToBounds = true
cell.userImage.layer.borderWidth = 0.3
cell.userImage.layer.borderColor = UIColor.lightGrayColor().CGColor
cell.time.text = "\((indexPath.row + 1) * 3)m"
return cell
}
}
}
extension TableViewController: UITableViewDelegate {
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
}
extension TableViewController: UISearchResultsUpdating {
func updateSearchResultsForSearchController(searchController: UISearchController) {
var filteredArray : [TextCell] = [TextCell]()
for textCell in self.items
{
if (textCell.userName.rangeOfString(searchController.searchBar.text) != nil ||
textCell.userPreferredName.rangeOfString(searchController.searchBar.text) != nil ||
textCell.userText.rangeOfString(searchController.searchBar.text) != nil)
{
filteredArray.append(textCell)
}
}
filteredTableData = filteredArray
self.tableView.reloadData()
}
}
Screenshots:
Bonus:
Is there any way of making those cells selectable?
If the searchText is empty, then set the filteredTableData to items
extension TableViewController: UISearchResultsUpdating {
func updateSearchResultsForSearchController(searchController: UISearchController) {
var filteredArray : [TextCell] = [TextCell]()
if searchController.searchBar.text == "" {
filteredArray = self.items
} else {
for textCell in self.items
{
if (textCell.userName.rangeOfString(searchController.searchBar.text) != nil ||
textCell.userPreferredName.rangeOfString(searchController.searchBar.text) != nil ||
textCell.userText.rangeOfString(searchController.searchBar.text) != nil)
{
filteredArray.append(textCell)
}
}
}
filteredTableData = filteredArray
self.tableView.reloadData()
}
}
What you can do is populate the filteredDataArray with no filtering at first, and then apply the filtering as it goes. You aren't setting filteredDataArray to anything until you start searching, so in the start of the search just set it to the full data set.
Bonus:
You can use tableViewDidSelectRowAtIndexPath to catch selection, and this should work even with the search table view.
Related
I could not find any way to add switch button in my tableview
I have a search bar which works perfectly,
and now I want to add a toggle button to enable "AND" "OR" filter in my search.
import Foundation
import UIKit
class TableViewController: UITableViewController, UISearchResultsUpdating {
var appDel: AppDelegate!
// var customTabBarItem:UITabBarItem = UITabBarItem(title: nil, image: UIImage(named: "my.png")?.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal), selectedImage: UIImage(named: "my.png"))
var filteredTableData = [String]()
var resultSearchController = UISearchController()
override func viewDidLoad() {
self.edgesForExtendedLayout = UIRectEdge.None
UIApplication.sharedApplication().statusBarHidden = true
appDel = UIApplication.sharedApplication().delegate as? AppDelegate
// tabBarItem = customTabBarItem
self.resultSearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self
controller.dimsBackgroundDuringPresentation = false
controller.searchBar.sizeToFit()
self.tableView.tableHeaderView = controller.searchBar
return controller
})()
// Reload the table
self.tableView.reloadData()
print("TableView called!!")
}
func updateSearchResultsForSearchController(searchController: UISearchController)
{
//refer to http://www.ioscreator.com/tutorials/add-search-table-view-tutorial-ios8-swift
filteredTableData.removeAll(keepCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchController.searchBar.text!)
let array = ((appDel.dataFetcher?.appTitles)! as NSArray).filteredArrayUsingPredicate(searchPredicate)
filteredTableData = array as! [String]
self.tableView.reloadData()
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (self.resultSearchController.active) {
return self.filteredTableData.count
}
else if appDel.dataFetcher?.appTitles == nil {
return 0
}else{
return (appDel.dataFetcher?.appTitles?.count)!
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("appCell", forIndexPath: indexPath)
let currentItem: String = (appDel.dataFetcher?.appTitles?[indexPath.row])!
if (self.resultSearchController.active) {
cell.textLabel?.text = filteredTableData[indexPath.row]
return cell
}
else {
cell.textLabel?.text = currentItem
cell.detailTextLabel?.text = String(indexPath.row)
return cell
}
}
}
Create a table header view by dragging a UIView to the top of the tableView, just above the first prototype cell. See this question for an example.
There you can place your UISwitchView and create an outlet to your VC.
I'm trying to implement a UISearchController to reference and search for items in a UITableView from another View Controller. In other words, I'd like to have search bar in VC1 to search for items in the UITableView of VC2.
The reason I'm doing this is because I'd like to have search functionality right when my app opens, i.e. in VC1 instead of having to go VC1->V2 to search for items in VC2.
I have already created some simple code to search for items in the UITableView of VC1, but how can I reference the UITableView of VC2 instead?
Here's my sample code for UISearchController implementation:
let tableData = ["One","Two","Three","Twenty-One"]
var filteredTableData = [String]()
var resultSearchController = UISearchController()
override func viewDidLoad() {
super.viewDidLoad()
self.resultSearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self
controller.dimsBackgroundDuringPresentation = false
controller.searchBar.sizeToFit()
self.tableView.tableHeaderView = controller.searchBar
return controller
})()
// Reload the table
self.tableView.reloadData()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (self.resultSearchController.active && self.resultSearchController.searchBar.text != "") {
return self.filteredTableData.count
}
else {
return self.tableData.count
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
if (self.resultSearchController.active && self.resultSearchController.searchBar.text != "") {
filteredTableData = tableData
cell.textLabel?.text = filteredTableData[indexPath.row]
return cell
}
else {
cell.textLabel?.text = tableData[indexPath.row]
return cell
}
}
func updateSearchResultsForSearchController(searchController: UISearchController)
{
filteredTableData.removeAll(keepCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchController.searchBar.text!)
let array = (tableData as NSArray).filteredArrayUsingPredicate(searchPredicate)
filteredTableData = array as! [String]
self.tableView.reloadData()
}
FYI: Both my VC1 and VC2 are connected via a UINavigationController
Thanks!
I'm getting errors whenever I try to reference the viewTable in the viewDidLoad AFTER I click on a cell to transition with the segue. Thanks a lot!!
Basically I can't use the segue unless I comment out the tableview references in view did load... but I need those in order to use the search bar and im sure it will cause problems on the way back...
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet var tableView: UITableView! {
didSet {
print("tableView is set")
}
}
let searchController = UISearchController(searchResultsController: nil)
let textCellIdentifier = "TextCell"
var buildings: [(String,String)] = []
var filteredBuildings = [(String,String)]()
var goToIndex: Int?
override func viewDidLoad() {
super.viewDidLoad()
print(tableView)
var buildingTuples = loadBuildings()
for tuple in buildingTuples {
self.buildings.append(tuple)
}
self.goToIndex = -1
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
definesPresentationContext = true
tableView!.tableHeaderView = searchController.searchBar
}
func filterContentForSearchText(searchText: String, scope: String = "All") {
filteredBuildings = buildings.filter { building in
return building.0.lowercaseString.containsString(searchText.lowercaseString)
}
self.tableView.reloadData()
}
// MARK: UITextFieldDelegate Methods
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//return self.buildings.count
if searchController.active && searchController.searchBar.text != "" {
return filteredBuildings.count
}
return buildings.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
/*
let cell = tableView.dequeueReusableCellWithIdentifier(textCellIdentifier, forIndexPath: indexPath)
let row = indexPath.row
cell.textLabel?.text = buildings[row].0
return cell
*/
let cell = tableView.dequeueReusableCellWithIdentifier(textCellIdentifier, forIndexPath: indexPath)
let tuple: (String, String)
if searchController.active && searchController.searchBar.text != "" {
tuple = filteredBuildings[indexPath.row]
} else {
tuple = buildings[indexPath.row]
}
cell.textLabel?.text = tuple.0
return cell
}
// MARK: UITableViewDelegate Methods
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
//tableView.deselectRowAtIndexPath(indexPath, animated: true)
let row = indexPath.row
self.goToIndex = indexPath.row
self.performSegueWithIdentifier("MainToLocation", sender: self)
//print(buildings[row].0)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "MainToLocation" {
let locationViewController = (segue.destinationViewController as! LocationViewController)
locationViewController.building = self.buildings[self.goToIndex!]
}
}
extension ViewController: UISearchResultsUpdating {
func updateSearchResultsForSearchController(searchController: UISearchController) {
filterContentForSearchText(searchController.searchBar.text!)
}
}
You can try this..
let destinationVC = self.storyboard!.instantiateViewControllerWithIdentifier("viewController") as! NextViewController
var alreadyPushed = false
if let vc = self.navigationController?.viewControllers {
for viewController in vc {
if let viewController = viewController as? NextViewController {
self.navigationController?.popToViewController(viewController, animated: true)
print("Push your controller")
alreadyPushed = true
break
}
}
}
if alreadyPushed == false {
self.navigationController?.pushViewController(destinationVC, animated: true)
}
I'm trying to add a search bar to a simple table view consisting of 7 cells of names and small description for each name.
As in the image here:
I made a class in swift file called Business, that has two attributes: Name and Des.
Here's the code in the view controller:
class FirstViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var TableView: UITableView!
var B = [Business]() //candies
var filteredNames = [Business]()
let searchController = UISearchController(searchResultsController: nil)
func filterContentForSearchText(searchText: String, scope: String = "All") {
filteredNames = B.filter { Bu in
return Bu.Name.lowercaseString.containsString(searchText.lowercaseString)
}
TableView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
B = [
Business(Name:"Mariah", Des:"I'm Here to help"),
Business(Name:"Nada", Des:"Hi"),
Business(Name:"Atheer", Des:"Hello"),
Business(Name:"Lojian", Des:"I can Help you"),
Business(Name:"Nadya", Des:"Hayat"),
Business(Name:"Omnia", Des:"Yahoo"),
Business(Name:"Eman", Des:"I have amazing stuff"),
Business(Name:"Amani", Des:"Yess")
]
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
definesPresentationContext = true
TableView.tableHeaderView = searchController.searchBar
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searchController.active && searchController.searchBar.text != "" {
return filteredNames.count
}
return B.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.TableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! CellTableViewCell
cell.NameLabel.text = B[indexPath.row].Name
cell.DescriptionLabel.text = B[indexPath.row].Des
let Bu: Business
if searchController.active && searchController.searchBar.text != "" {
Bu = filteredNames[indexPath.row]
} else {
Bu = B[indexPath.row]
}
cell.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator
return cell
}
}
extension FirstViewController: UISearchResultsUpdating {
func updateSearchResultsForSearchController(searchController:
(UISearchController) {
filterContentForSearchText(searchController.searchBar.text!)
}
}
I followed this tutorial to do that:
https://www.raywenderlich.com/113772/uisearchcontroller-tutorial
I don't know whay when I tried to search in simulator the result is always the first cell: Mariah
What's wrong with the code?
You don't use the search result to populate the cells. Replace you cellForRowAtIndexPath with this:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.TableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! CellTableViewCell
let Bu: Business
if searchController.active && searchController.searchBar.text != "" {
Bu = filteredNames[indexPath.row]
} else {
Bu = B[indexPath.row]
}
cell.NameLabel.text = Bu.Name
cell.DescriptionLabel.text = Bu.Des
cell.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator
return cell
}
And, don't use capital first letters for properties.
I have a UIViewControllerand I added UITableViewController in it and I am adding search bar programmatically. I am able to print the result after searching on the console but when I click on the search bar the custom table cells are still populated and search doesn't update them at all.
I have print statement in the override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell to check if the search controller is active and it never prints that statement.
import UIKit
import Parse
class MasterViewController: UITableViewController, UISearchResultsUpdating, UISearchBarDelegate {
#IBOutlet var mTableView: UITableView!
var detailViewController: DetailViewController? = nil
var objects = [AnyObject]()
var resultSearchController = UISearchController()
var filteredData : NSMutableArray = []
var sectionInTable : NSMutableArray = ["Grand Haven 9", "Holland 7"]
var movieTitle : NSMutableArray = ["The Hunger Game 2","Creed"]
var movieTimeSection1 : NSMutableArray = ["11:00am, 11:40, 12:10pm, 1:25, 2:30, 6:05, 6:55, 7:30, 9:05", "11:10am, 11:50, 12:20pm, 2:25, 3:30, 6:05, 6:55, 8:30, 10:05"]
var movieTimeSection2 = ["Title": "Martian", "Theatre": "Holland 7", "Time": "11:00am, 11:40, 12:10pm, 1:25, 2:30, 6:05, 6:55, 7:30, 9:05"]
var onlineMovieTitle : NSMutableArray = []
var movieSection1 : NSMutableArray = []
var movieSection2 : NSMutableArray = []
override func viewDidLoad() {
super.viewDidLoad()
print(movieSection1)
print(movieSection2)
self.tableView.separatorColor = UIColor.cloudsColor()
self.tableView.backgroundColor = UIColor.cloudsColor()
self.resultSearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self
controller.dimsBackgroundDuringPresentation = false
controller.searchBar.delegate = self
controller.searchBar.sizeToFit()
controller.preferredStatusBarStyle() == UIStatusBarStyle.LightContent
controller.hidesNavigationBarDuringPresentation = false
self.mTableView.tableHeaderView = controller.searchBar
return controller
})()
//self.resultSearchController.searchBar.endEditing(true)
}
func updateSearchResultsForSearchController(searchController: UISearchController){
filteredData.removeAllObjects()
let searchPredicate = NSPredicate(format: "Title CONTAINS %#", searchController.searchBar.text!.uppercaseString)
let array = (movieSection1 as NSArray).filteredArrayUsingPredicate(searchPredicate)
let array2 = (movieSection2 as NSArray).filteredArrayUsingPredicate(searchPredicate)
//let array2 = (movieTimeSection1 as NSArray).filteredArrayUsingPredicate(searchPredicate)
filteredData.addObjectsFromArray(array)
filteredData.addObjectsFromArray(array2)
print(filteredData)
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.resultSearchController.searchBar.hidden = false
}
// MARK: - Segues
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow {
}
}
}
// MARK: - Table View
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var count : Int = 0
if (self.resultSearchController.active) {
return 2
}
else {
if section == 0 {
count = movieSection1.count
} else if section == 1 {
count = movieSection2.count
}
}
return count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell : MainTableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! MainTableViewCell
//Fancy Cells
var corners : UIRectCorner = UIRectCorner.AllCorners
if (tableView.numberOfRowsInSection(indexPath.section) == 1) {
corners = UIRectCorner.AllCorners
} else if (indexPath.row == 0) {
corners = UIRectCorner.TopLeft.union(UIRectCorner.TopRight)
} else if (indexPath.row == tableView.numberOfRowsInSection(indexPath.section) - 1) {
corners = UIRectCorner.BottomLeft.union(UIRectCorner.BottomRight)
}
cell.configureFlatCellWithColor(UIColor.greenSeaColor(), selectedColor: UIColor.cloudsColor(), roundingCorners: corners)
//Fancy Buttons
cell.cornerRadius = 7
if indexPath.row == 0 {
cell.trailorButton.addTarget(self, action: "openHunger:", forControlEvents: UIControlEvents.TouchUpInside)
}else if indexPath.row == 1{
cell.trailorButton.addTarget(self, action: "openCreed:", forControlEvents: UIControlEvents.TouchUpInside)
}
cell.trailorButton.layer.cornerRadius = 5
cell.trailorButton.tintColor = UIColor.whiteColor()
let movieNameStr = movieTitle[indexPath.row] as! String
cell.movieImage.image = UIImage(named: movieNameStr)
//Cell Data Insertion
if (self.resultSearchController.active){
print("search ---------------")
cell.movieName.text = filteredData[indexPath.row].valueForKey("Title") as? String
cell.movieDetail.text = filteredData[indexPath.row].valueForKey("Time") as? String
return cell
}else {
print("cell")
if indexPath.section == 0 {
cell.movieName.text = movieSection1[indexPath.row].valueForKey("Title") as? String
cell.movieDetail.text = movieSection1[indexPath.row].valueForKey("Time") as? String
} else if indexPath.section == 1 {
cell.movieName.text = movieSection2[indexPath.row].valueForKey("Title") as? String
cell.movieDetail.text = movieSection2[indexPath.row].valueForKey("Time") as? String
}
return cell
}
//return cell
}
#IBAction func openHunger(sender: AnyObject) {
UIApplication.sharedApplication().openURL(NSURL(string: "https://youtu.be/n-7K_OjsDCQ")!)
}
#IBAction func openCreed(sender: AnyObject) {
UIApplication.sharedApplication().openURL(NSURL(string: "https://youtu.be/fCBzWLVQgk8")!)
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return self.sectionInTable[section] as? String
}
}
Any help will be highly appreciated. Thanks
I missed it and thanks to Larcerax the problem was solved by adding mTableView.reloadData()