I have tableview with alphabetic sections from my database and I want to add search bar but i can't figure it out how to filter the data and implement it in the tableview.
My database store inside two structs:
one struct holding all the data.
second struct gets the first letter for the sections and the first struct as array.
My structs:
struct SentenceInfo { // First struct (holds all the data)
let name: String
let detail: String
let sentence: String
init(name: String, detail: String, sentence: String) {
self.name = name
self.detail = detail
self.sentence = sentence
}
}
struct SentenceNameSection { // Second struct (first letter and array of the first struct)
var firstLetter: String
var crimes: [SentenceInfo]
init(title: String, objects: [SentenceInfo]) {
firstLetter = title
crimes = objects
}
}
My tableView:
var sections : [SentenceNameSection]!
var crimeData = [SentenceNameSection]()
var filteredData = [SentenceNameSection]()
var shouldShowSearchResults = false
var searchController: UISearchController!
func updateSearchResults(for searchController: UISearchController) {
let searchString = searchController.searchBar.text
filteredData = crimeData.filter({ (crime) -> Bool in
let crimeMatch: String = crime // Error about types
return ((crimeMatch.range(of: searchString!) != nil))
})
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: sentenceTableViewCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifer, for: indexPath) as! sentenceTableViewCell
let crime: SentenceInfo = sections[indexPath.section].crimes[indexPath.row]
cell.nameLabel.text = crime.name
cell.detailLabel.text = crime.detail
cell.sentenceLabel.text = crime.sentence
return cell
}
First of all crimeData contains SentenceNameSection which cannot be compared to String
Apart from that to filter the data source array including sections you have to use a repeat loop and create new SentenceNameSection items
This code searches for all three properties in the SentenceInfo struct
let searchString = searchController.searchBar.text!
filteredData.removeAll() // is mandatory to empty the filtered array
for section in crimeData {
let filteredContent = section.crimes.filter { $0.name.range(of: searchString) != nil
|| $0.detail.range(of: searchString) != nil
|| $0.sentence.range(of: searchString) != nil
}
if !filteredContent.isEmpty {
filteredData.append(SentenceNameSection(title: section.firstLetter, objects: filteredContent))
}
}
Note: Of course you have to handle the search case in all appropriate table view data source and delegate methods.
For Swift 3 , below is the sample code
Struct BookDetails{
var title:String?
var author:String?
}
var filteredSearch:[BookDetails] = []
filteredSearch = self.bookDetails.filter { (data) -> Bool in
return data.title?.range(of: searchText, options: String.CompareOptions.caseInsensitive) != nil || data.author?.range(of: searchText, options: String.CompareOptions.caseInsensitive) != nil
}
Related
I'm trying to parse to following JSON into a tableView : https://www.pathofexile.com/api/trade/data/items
I succeeded in parsing the first array, but I'm unable to parse the key "entries"...
Here's my code, with the data structure I defined :
import UIKit
struct ItemCategories: Codable {
var result: [ItemCategory]
}
struct ItemCategory: Codable {
var label: String
var entries: [Item]
}
struct Item: Codable {
// empty struct
}
class ViewController: UITableViewController {
let urlString = "https://www.pathofexile.com/api/trade/data/items"
var categories = [ItemCategory]()
override func viewDidLoad() {
super.viewDidLoad()
title = "Path of Data"
navigationController?.navigationBar.prefersLargeTitles = true
parse()
}
func parse() {
guard let url = URL(string: urlString) else { return }
guard let data = try? Data(contentsOf: url) else { return }
let decoder = JSONDecoder()
guard let jsonItemCategories = try? decoder.decode(ItemCategories.self, from: data) else { return }
categories = jsonItemCategories.result
tableView.reloadData()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return categories.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
var categoryName = categories[indexPath.row].label
if categoryName == "" { categoryName = "Unknown" }
cell.textLabel?.text = categoryName
cell.textLabel?.textColor = .systemOrange
let numberOfItemsInCategory = String(categories[indexPath.row].entries.count)
cell.detailTextLabel?.text = numberOfItemsInCategory + " items"
return cell
}
}
The struct Item is empty, because when I try to add variable corresponding to the keys in the JSON, then the whole parsing fail (the tableView displays nothing).
When the struct Item is empty, then the parsing succeed and the tableView is able to display the different categories. It even display the number of items for each "entries" thanks to :
let numberOfItemsInCategory = String(categories[indexPath.row].entries.count)
cell.detailTextLabel?.text = numberOfItemsInCategory + " items"
Can someone explain why ? Ideally I would like to display the content of "entries" when the rows are tapped, but I can't figure out how for the moment.
Thanks for you help :)
screenshot
#Laurent Delorme Your Struct Item should be like below, try with this,
struct Item: Codable {
let name: String?
let type: String?
let text: String?
let flags: FlagsRepresentation?
enum CodingKeys: String, CodingKey {
case name
case type
case text
case flags
}
}
struct FlagsRepresentation: Codable {
let unique: Bool?
enum CodingKeys: String, CodingKey {
case unique
}
}
Does anyone have any info on how to incorporate Firebase into a UISearchController delegate? I can't find any solid info on it. There may possibly be thousands of employees.
I know how to use the search controller delegates updateSearchResultsForSearchController and using a NSPredicate to filter what I'm looking for if I was using NSUserDefaults but using Firebase I'm uncertain.
I've added some more code to my question
I have a custom data model object saved in FirebaseDatabase and I'd like to search on all of the following properties within the object.
lastName
idNumber
deptNumber
position
Searching any of these properties should first show a partial string inside the table cells until the entire string i'm looking for is shown. So if I typed in the letter "S" then all employee last names beginning with "S" should show. If I enter "Sa" the in would filter to those letters". From my understanding I should use "\u{f8ff}" to get the partial search string but no data is returned.
Anyhow here's all the code
My object is:
class Employee{
var firstName: String?
var lastName: String?
var idNumber: String?
var deptNumber: String?
var position: String?
}
My paths
-root
-users
-uid
|_"email":"emailAddress"
|_"userID":"uid"
|_"firstName":"firstName"
|_"lastName":"lastName"
-employees
-hireDate
-uid //this is the same uid from the users node so I know who's who
|_"firstName":"firstName"
|_"lastName":"lastName"
|_"idNum":"idNumber"
|_"deptNumber":"deptNumber"
|_"position":"position"
My rules:
What's happening here is the day an employee is hired they are asked to create a company account using their email address and pw.
At the same time a "employees" path is created with a child being a "hireDate" path and finally the employees "uid" path. This employee "uid" is the path I want to search on from the "employees" node
{
"rules": {
"users" : {
"$uid" : {
".read": true,
".write": "auth != null && auth.uid == $uid"
}
},
"employees": {
"$hireDate": {
"$uid": {
".read": true,
".indexOn": ["lastName", "idNumber", "deptNumber", "position"]
}
}
}
}
}
My searchController
import UIKit
class SearchController: UIViewController{
#IBOutlet var tableView: UITableView!
var searchController: UISearchController!
var employeesToFilter = [Employee]()
var filteredSearchResults = [Employee]()
override func viewDidLoad() {
super.viewDidLoad()
self.searchController = UISearchController(searchResultsController: nil)
self.tableView.delegate = self
//all searchController properties get set here no need to include them though
let ref = FIRDatabase.database().reference()
let employeeRef = ref.child("employees")
employeeRef?.queryOrderedByChild("lastName").queryStartingAtValue("\u{f8ff}").queryLimitedToFirst(20).observeEventType(.ChildAdded, withBlock: {
(snapshot) in
if let dict = snapshot.value as? [String:AnyObject]{
let firstName = dict["firstName"] as! String
let lastName = dict["lastName"] as! String
let idNumber = dict["idNumber"] as! String
let deptNumber = dict["deptNumber"] as! String
let position = dict["position"] as! String
let employee = Employee()
employee.firstName = firstName
employee.lastName = lastName
employee.idNumber = idNumber
employee.deptNumber = deptNumber
employee.position = position
self.employeesToFilter.append(employee)
}
})
self.tableView.reloadData()
}
override func viewDidAppear(animated: Bool) {
self.searchController.active = true
}
deinit {
self.searchController = nil
}
}
//MARK:- TableView Datasource
extension SearchBuildingController: UITableViewDataSource, UITableViewDelegate{
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.filteredSearchResults.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("SearchCell", forIndexPath: indexPath) as! SearchCell
let searchString = self.filteredSearchResults[indexPath.row]
cell.firstNameLabel.text = searchString.firstName
cell.lastNameLabel.text = searchString.lastName
cell.idNumberLabel.text = searchString.idNumber
cell.deptNumberLabel.text = searchString.deptNumber
cell.positionLabel.text = searchString.position
return cell
}
}
//MARK:- SearchController Delegates
extension SearchController: UISearchResultsUpdating, UISearchBarDelegate, UISearchControllerDelegate{
func searchBarTextDidBeginEditing(searchBar: UISearchBar) {
tableView.reloadData()
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
self.employeesToFilter.removeAll(keepCapacity: false)
self.filteredSearchResults.removeAll(keepCapacity: false)
let searchText = self.searchController.searchBar.text
let searchPredicate = NSPredicate(format: SELF.lastName CONTAINS [c] %# OR SELF.idNumber CONTAINS [c] %# OR SELF.deptNumber CONTAINS[c] %# OR SELF.position CONTAINS [c] %#", searchText!, searchText!, searchText!, searchText!)
let array = (self.employeesToFilter as NSArray).filteredArrayUsingPredicate(searchPredicate)
self.filteredSearchResults = array as! [Employee]
tableView.reloadData()
}
}
Here is an example of how I have accomplished this using Firebase building a list of campuses. This method loads all of the data that is in the table view up front making it easy to search and filter.
My campus object is pretty simple with an id and a name.
struct Campus {
var id: String
var name: String
}
In the view controller I have two arrays. One is to hold the list of all campuses returned and the other array is for the filtered campuses.
let campuses = [Campus]()
let filteredCampuses = [Campus]()
I then called a method that I had set up to load the campuses from Firebase.
override func viewDidLoad() {
...
getAllCampusesFromFirebase() { (campuses) in
self.campuses = campuses
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
}
}
Then when performing the search I filter out the campuses comparing the campus name to the search text from the search bar.
func updateSearchResultsForSearchController(searchController: UISearchController) {
guard let searchText = searchController.searchBar.text else {
return
}
filteredCampuses = campuses.filter { campus in
return campus.name.lowercaseString.containsString(searchText.lowercaseString)
}
tableView.reloadData()
}
If you are not loading all of the data up front then Firebase provides some handy methods to call that you can use to filter the data based on the reference path. https://firebase.google.com/docs/database/ios/lists-of-data
queryStarting(atValue) or queryStarting(atValue:childKey:) would probably be the one that you'd want to use in this case.
ref.queryStarting(atValue: Any?)
ref.queryStarting(atValue: Any?, childKey: String?)
I have three objects nested via lists like this:
class Canteen: Object {
dynamic var name: String?
let lines = List<Line>()
}
class Line: Object {
dynamic var name: String?
let meals = List<Meal>()
}
class Meal: Object {
dynamic var name: String?
dynamic var vegan: Bool = false
}
Getting all canteens with all the lines and meals is no problem. What im doing right now is this:
let predicate = NSPredicate(format: "name == %#", selectedCanteenType.rawValue)
canteens = realm.objects(Canteen).filter(predicate)
But now i only need the meals which are vegan. So im looking to get the selected canteen with all the lines, but only with meals which are vegan. Is this possible in realm, to filter lists in retrieved objects?
Realm doesn't have any sort of concept of a deep-filtered view, so you can't have a Results<Canteen> which restricts the Lists contained in related objects to vegan meals.
There are several similar things which you can do. You could add inverse relationship properties, and then query Meal objects instead:
class Canteen: Object {
dynamic var name: String?
let lines = List<Line>()
}
class Line: Object {
dynamic var name: String?
let meals = List<Meal>()
let canteens = LinkingObjects(fromType: Canteen.self, property: "lines")
}
class Meal: Object {
dynamic var name: String?
dynamic var vegan: Bool = false
let lines = LinkingObjects(fromType: Line.self, property: "meals")
}
let meals = realm.objects(Meal).filter("vegan = true AND ANY lines.canteens.name = %#", selectedCanteenType.rawValue)
(Or rather, you will be able to once Realm 0.102.1 is out; currently this crashes).
If you just need to iterate over the meals but need to do so from the Canteen down, you could do:
let canteens = realm.objects(Canteen).filter("name = %# AND ANY lines.meals.vegan = true", selectedCanteenType.rawValue)
for canteen in canteens {
for line in canteen.lines.filter("ANY meals.vegan = true") {
for meal in line.meals.filter("vegan = true") {
// do something with your vegan meal
}
}
}
This unfortunately has some duplication due to needing to repeat the filter for each level of the references.
Try this:
let predicate = NSPredicate(format: "name == %#", "")
var canteens: [Canteen] = realm.objects(Canteen).filter(predicate).map { can in
// Iterate through all the Canteens
let lines: [Line] = can.lines.map { (line: Line) in
// Iterate through all the lines in each canteene
// Filter all the Meals that are NOT vegan
let meals = line.meals.filter { $0.vegan == true }
line.meals = List<Meal>(meals)
return line
}
can.lines = List<Line>(lines)
return can
}
Realm allows it to use functions as parameter for the filtering. So this is my solution which im currently using.
The two filter functions:
func vegetarianFilter(_ meal: Meal) -> Bool {
if showVegetarianOnly {
if(meal.veg || meal.vegan){
return true
}
return false
}
return true
}
func filterEmptyLines(_ line: Line) -> Bool {
if(line.meals.filter(vegetarianFilter).count > 0){
return true
}
return false
}
The functions filter all meals which are not vegetarian or vegan when the user has selected showVegetarianOnly = true. Also it filters all lines which than have no meal left (nothing is vegetarian or vegan).
Most important functions of the TableView:
override func numberOfSections(in tableView: UITableView) -> Int {
return canteenDay?.lines.filter(filterEmptyLines).count ?? 0
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return canteenDay?.lines.filter(filterEmptyLines)[section].meals.filter(vegetarianFilter).count ?? 0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
let meal = canteenDay!.lines.filter(filterEmptyLines)[indexPath.section].meals.filter(vegetarianFilter)[indexPath.row]
cell.textLabel?.text = meal.meal
return cell
}
I am receiving a JSON file from a remote server and I can display the result in a label. The JSON data is working fine when I call function processJSONData() and the tableview works fine with a simple array. How can I incorporate both to display the result from the JSON file in the tableview? Kindly look at the code below and edit. Many thanks:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var countryLabel: UILabel!
#IBOutlet weak var capitalLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
//processJSONData()
self.myTableView.registerClass(UITableViewCell.self,forCellReuseIdentifier: "cell")
self.myTableView.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func processJSONData(){
let urlPath = "http://dubaisinan.host22.com/service1.php"
let url : NSURL = NSURL(string: urlPath)!
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url,completionHandler: {(data, respose, error) -> Void in
if error != nil {
println(error)
}
else {
self.abc(data)
}
})
task.resume()
}
func abc(data:NSData)
{
var parseError: NSError?
let result:AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &parseError);
if(parseError == nil){
if let dictResult = result as? NSArray{
dispatch_async(dispatch_get_main_queue()) {
self.countryLabel.text = dictResult[2]["Capital"] as? String
}
}
}
}
#IBOutlet weak var myTableView: UITableView!
var items = ["One","Two", "Three","Four"]
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:UITableViewCell = self.myTableView
.dequeueReusableCellWithIdentifier("cell") as UITableViewCell
cell.textLabel?.text = self.items[indexPath.row]
return cell
}
}
I don't see you assign your parsing result to global "items" and reload tableview with new data anywhere.
could be done here
if let dictResult = result as? NSArray{
self.items = dictResult
self.myTableView.reloadData()
///the rest of the code
}
You have to save the JSON data into a class-level variable, which you will define outside of any function, similar to how you defined "items". Assuming you have a list of countries with the capital of each, this might look like so:
var countryAndCapitalData = [(country: String, capital: String)]()
This could be improved by first defining a struct to contain your data:
struct CountryInfo
{
name: String
capital: String
init(name:String, capital:String)
{
self.name = name
self.capital = capital
}
}
which lets you define your data array as an array of CountryInfo:
var countryAndCapitalData = [CountryInfo]()
Then in your "abc" function (which I insist you rename to something like processCountryData), store the pairs of country name + capital name strings in countryAndCapitalData. For example:
countryAndCapitalData.append(CountryInfo(countryName, capitalName))
Use a For loop to loop through values in dictResult. Creating countryName and capitalName depends on the structure of your JSON, but from your example it might look like this:
for countryDictionary in dictResult[2]
{
if let countryName = countryDictionary["country"], let capitalName = countryDictionary["capital"]
{
countryAndCapitalData.append(CountryInfo(countryName, capitalName))
}
}
Then in tableView.cellForRowAtIndexPath, populate the cell label(s) with countryAndCapitalData[indexPath.row].name and countryAndCapitalData[indexPath.row].capital.
And finally, be sure to reload the table after the loop (thanks Eugene):
dispatch_async(dispatch_get_main_queue()) {
self.myTableView.reloadData()
}
Apologies for any compilation errors, as I'm typing this from a Windows machine.
You should update your items property in abc method call and then refresh the table:
func abc(data: NSData) {
// Do something with data
items = .. // processed data
}
var items: [String]? {
didSet {
NSOperationQueue.mainQueue.addOperationWithBlock {
self.tableView.reloadData()
}
}
}
The old UISearchDisplayController class is now deprecated and instead we have to use the new UISearchController. There used to be a property in the old class called "SearchResultsTableView" but it's gone from the new class.
I populate a table with data and all works as intended - including segueing each row's details to another scene. I throw a search bar in there (programmatically - using the new searchController) and it successfully reloads the original table with any found results.
HOWEVER, when touching a selected row after a search, the segue passed along is that of the original table row that happens to be in the same position of the one touched now! (i.e. if I choose the current second row of a search, the next scene will segue the details of the second row of the original table!) That's because despite the data in the rows are being successfuly repopulated with the search data, the index numbers are still those of the old data.
It used to be with the old type that we would check this as such:
if (self.resultSearchController.active) {
let indexPath = self.searchDisplayController!.searchResultsTableView.indexPathForSelectedRow()
} else {
let indexPath = self.tableView.indexPathForSelectedRow()
So I think that with the old UISearchDisplayController class you actually got a new table, whereas with the new SearchController Class you only get new rows inside the old table? This totaly doesn't make sense !
Here is my full code per request:
import UIKit
import Foundation
class secondTableViewController: UITableViewController, UITableViewDelegate, UITableViewDataSource, UISearchResultsUpdating {
var filteredTableData = [String]()
var resultSearchController = UISearchController()
//these 2 are standard for the title and subtitle
var TableTitle:Array< String > = Array < String >()
var TableSub:Array< String > = Array < String >()
//the following are for my seque to next scene
var the_fname:Array< String > = Array < String >()
var the_basics:Array< String > = Array < String >()
var the_p_method:Array< String > = Array < String >()
var the_seats:Array< String > = Array < String >()
var the_notes:Array< String > = Array < String >()
var the_tableData:Array< String > = Array < String >()
override func viewDidLoad() {
tableView.delegate = self
tableView.dataSource = self
self.title = currentBus
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()
var url = "http://the_path_to_my_json_file"
get_data_from_url(url)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// 2
if (self.resultSearchController.active) {
return self.filteredTableData.count
}
else {
return TableTitle.count
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("secondtableCell", forIndexPath: indexPath) as! UITableViewCell
// Configure the cell...
if (self.resultSearchController.active) {
cell.textLabel?.text = filteredTableData[indexPath.row]
//cell.detailTextLabel?.text = TableSub[indexPath.row]
}else{
cell.textLabel?.text = TableTitle[indexPath.row]
cell.detailTextLabel?.text = TableSub[indexPath.row]
}
return cell
}
func get_data_from_url(url:String)
{
let httpMethod = "GET"
let timeout = 15
let url = NSURL(string: url)
let urlRequest = NSMutableURLRequest(URL: url!,
cachePolicy: .ReloadIgnoringLocalAndRemoteCacheData,
timeoutInterval: 15.0)
let queue = NSOperationQueue()
NSURLConnection.sendAsynchronousRequest(
urlRequest,
queue: queue,
completionHandler: {(response: NSURLResponse!,
data: NSData!,
error: NSError!) in
if data.length > 0 && error == nil{
let json = NSString(data: data, encoding: NSASCIIStringEncoding)
self.extract_json(json!)
}else if data.length == 0 && error == nil{
println("Nothing was downloaded")
} else if error != nil{
println("Error happened = \(error)")
}
}
)
}
func extract_json(data:NSString)
{
var parseError: NSError?
let jsonData:NSData = data.dataUsingEncoding(NSASCIIStringEncoding)!
let json: AnyObject? = NSJSONSerialization.JSONObjectWithData(jsonData, options: nil, error: &parseError)
if (parseError == nil)
{
if let my_pass_list = json as? NSArray
{
for (var i = 0; i < my_pass_list.count ; i++ )
{
if let each_pass = my_pass_list[i] as? NSDictionary
{
if let fname = each_pass["fname"] as? String
{
if let lname = each_pass["lname"] as? String
{
if let numofseats = each_pass["numofseats"] as? String
{
if let showed_up = each_pass["showed_up"] as? String
{
if let res_id = each_pass["resnum"] as? String
{
if let res_notes = each_pass["res_notes"] as? String
{
if let payment_description = each_pass["payment_description"] as? String
{
// the_tableData.append(fname)
the_fname.append(fname)
the_basics.append(fname + " " + lname)
the_p_method.append(payment_description)
the_seats.append(numofseats)
the_notes.append(res_notes)
TableTitle.append(fname + " " + lname)
TableSub.append("Seats Reserved: " + numofseats + ". Showed Up: " + showed_up + ". Notes:" + res_notes)
the_tableData = TableTitle
}
}
}
}
}
}
}
}
}
}
}
do_table_refresh();
}
func do_table_refresh()
{
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
return
})
}
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using [segue destinationViewController].
var thirdScene = segue.destinationViewController as! customer_details_View_Controller
if let indexPath = self.tableView.indexPathForSelectedRow() {
/*
so what I'm missing is to be able to check
if (self.resultSearchController.active) {
and if yes have indexPath be the self.resultSearchController.resultSearchTableView.indexPathForSelectedRow() {
or something of that nature
*/
thirdScene.dotrav = todayString
thirdScene.from = currentBus
thirdScene.basics = the_basics[indexPath.row]
thirdScene.p_method = the_basics[indexPath.row]
thirdScene.seats = the_tableData[indexPath.row]
thirdScene.notes = the_notes[indexPath.row]
}
// Pass the selected object to the new view controller.
}
func updateSearchResultsForSearchController(searchController: UISearchController)
{
filteredTableData.removeAll(keepCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchController.searchBar.text)
let array = (the_tableData as NSArray).filteredArrayUsingPredicate(searchPredicate)
filteredTableData = array as! [String]
self.tableView.reloadData()
}
}
You need to account for the fact that you are going to have different data in your tableView depending on the search result. You can still use self.tableView.indexPathForSelectedRow.
What I do, is keep a reference to my base data, and then keep a reference to my filtered data, and display my filtered data in the tableView at all times. If my searchBar has no text, then my filtered data is equal to my base data.
Example:
class MyTableViewController: UITableViewController, UISearchResultsUpdating {
var data: [String] = ["One", "Two", "Three", "Four", "Five"]
var filteredData: [String]!
var searchController: UISearchController!
override func viewDidLoad() {
super.viewDidLoad()
setUpSearchController()
setFilteredDataForCurrentSearch()
}
private func setUpSearchController() {
searchController = UISearchController(searchResultsController: nil)
searchController.searchResultsUpdater = self
self.tableView.tableHeaderView = searchController.searchBar
}
private func setFilteredDataForCurrentSearch() {
if let searchString = searchController.searchBar.text where !searchString.isEmpty {
filteredData = data.filter({ (string: String) -> Bool in
return searchString.rangeOfString(string, options: NSStringCompareOptions.CaseInsensitiveSearch) != nil
})
} else {
filteredData = data
}
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
setFilteredDataForCurrentSearch()
}
}
Now, you can implement all of your UITableViewDataSource and UITableViewDelegate methods using the filteredData.
In prepareForSegue, you retrieve the correct selected object like:
let indexPath = tableView.indexPathForSelectedRow()
let selectedObject = filteredData[indexPath.row]