The app shows a list of cars parts (car part, type, year, country) it is working in a tableview with a search bar.
Now I decided to add a scope bar to filter the results, and I am sure I messed up with the code. There are many errors in the lines to add the scopeBar. It is the last 20 lines, after the comment //ScopeBar try
, other than this last lines I added a code in the viewDidLoad()
to show the titles I want.
What am I doing wrong? Any help is more than welcome, I am trying to fix this for 2 days already no luck.
import UIKit import CoreData class DictionaryTableViewController: UITableViewController, NSFetchedResultsControllerDelegate, UISearchResultsUpdating { var searchController:UISearchController! var searchResults:[Dictionary] = [] private var dictionaryItems:[Dictionary] = [] private var cockpitDict = [String: [Dictionary]]() var sectionTitles = [String]() var fetchResultController:NSFetchedResultsController! override func viewDidLoad() { super.viewDidLoad() // ScopeBar Try searchController.searchBar.scopeButtonTitles = ["All", "type", "year", "country"] tableView.tableHeaderView = searchController.searchBar // Load menu items from database if let managedObjectContext = (UIApplication.sharedApplication().delegate as? AppDelegate)?.managedObjectContext { let fetchRequest = NSFetchRequest(entityName: "DictionaryEntity") do { dictionaryItems = try managedObjectContext.executeFetchRequest(fetchRequest) as! [Dictionary] } catch { print("Failed to retrieve record") print(error) } } searchController = UISearchController(searchResultsController: nil) tableView.tableHeaderView = searchController.searchBar searchController.searchResultsUpdater = self searchController.dimsBackgroundDuringPresentation = false searchController.searchBar.placeholder = "Search ..." navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .Plain, target: nil, action: nil) // Enable self sizing cells tableView.estimatedRowHeight = 100.0 tableView.rowHeight = UITableViewAutomaticDimension createCockpitDict() } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } func createCockpitDict(){ for item in dictionaryItems { guard let word = item.word else { break } // Get the first letter of the word and build the dictionary let wordKey = word.substringToIndex(word.startIndex.advancedBy(1)) if var cockpitItems = cockpitDict[wordKey] { cockpitItems.append(item) cockpitDict[wordKey] = cockpitItems } else { cockpitDict[wordKey] = [item] } } // Get the section titles from the dictionary's keys and sort them in ascending order sectionTitles = [String](cockpitDict.keys) sectionTitles = sectionTitles.sort({ $0 < $1 }) } // create a standard way to get a Dictionary from a index path func itemForIndexPath (indexPath: NSIndexPath) -> Dictionary? { var result: Dictionary? = nil if searchController.active { result = searchResults[indexPath.row] }else{ let wordKey = sectionTitles[indexPath.section] if let items = cockpitDict[wordKey]{ result = items[indexPath.row] } } return result } // MARK: - Table view data source override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? { return sectionTitles[section] } override func numberOfSectionsInTableView(tableView: UITableView) -> Int { //assume a single section after a search return (searchController.active) ? 1 : sectionTitles.count } override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { if searchController.active { return searchResults.count } else { // Return the number of rows in the section. let wordKey = sectionTitles[section] if let items = cockpitDict[wordKey] { return items.count } return 0 } } override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! DictionaryTableViewCell //let dictionary = (searchController.active) ? searchResults[indexPath.row]: dictionaryItems[indexPath.row] if let dictionary = itemForIndexPath(indexPath){ cell.wordLabel.text = dictionary.word cell.definitionSmallLabel.text = dictionary.definition cell.typeLabel.text = dictionary.type cell.yearLabel.text = dictionary.year cell.countryLabel.text = dictionary.country }else{ print("Cell error with path\(indexPath)") } return cell } override func sectionIndexTitlesForTableView(tableView: UITableView) -> [String]? { return sectionTitles } // Override to support conditional editing of the table view. override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool { // Return false if you do not want the specified item to be editable. if searchController.active{ return false }else{ return true } } override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { if segue.identifier == "showDictionaryDetail" { if let indexPath = tableView.indexPathForSelectedRow { let destinationController = segue.destinationViewController as! DictionaryDetailViewController if let dictionary = itemForIndexPath(indexPath){ destinationController.dictionary = dictionary }else{ print("Segue error with path \(indexPath)") } searchController.active = false } } } func updateSearchResultsForSearchController(searchController: UISearchController) { if let searchText = searchController.searchBar.text { filterContentForSearchText(searchText) tableView.reloadData() } } func filterContentForSearchText(searchText: String) { searchResults = dictionaryItems.filter({ (dictionary:Dictionary) -> Bool in let wordMatch = dictionary.word!.rangeOfString(searchText, options: NSStringCompareOptions.CaseInsensitiveSearch) return wordMatch != nil }) } } //ScopeBar try: all lines below got many errors I can not figure out how to fix it :( func filterContentForSearchText(searchText: String, scope: String = "All") { dictionaryItems = cockpitDict.filter({( cockpitDict : Dictionary) -> Bool in let categoryMatch = (scope == "All") || (cockpitDict.category == scope) return categoryMatch && cockpitDict.name.lowercaseString.containsString(searchText.lowercaseString) }) tableView.reloadData() } extension DictionaryTableViewController:UISearchBarDelegate { // MARK: - UISearchBar Delegate func searchBar(searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) { filterContentForSearchText(searchBar.text!, scope: searchBar.scopeButtonTitles![selectedScope]) } } extension DictionaryTableViewController: UISearchResultsUpdating { // MARK: - UISearchResultsUpdating Delegate func updateSearchResultsForSearchController(searchController: UISearchController) { let searchBar = searchController.searchBar let scope = searchBar.scopeButtonTitles![searchBar.selectedScopeButtonIndex] filterContentForSearchText(searchController.searchBar.text!, scope: scope) } }
2 Answers
Answers 1
There are a few issues with this code. For starters, as others have pointed out, var searchResults:[Dictionary] = []
isn't valid syntax. A Dictionary
in Swift requires a type specification, such as Dictionary<String: AnyObject>
. I'm guessing based off the rest of your code that you've named a custom entity named Dictionary
, which may be causing the confusion.
The next issue I see is that on lines 8 and 10, you need to initialize the array, using ()
, i.e. var searchResults:[Dictionary] = []()
. (Again, this is assuming you fix the issue of declaring your Dictionary
type correctly.)
Another issue is your filter
statement on line 192:
dictionaryItems = cockpitDict.filter({( cockpitDict : Dictionary) -> Bool in let categoryMatch = (scope == "All") || (cockpitDict.category == scope) return categoryMatch && cockpitDict.name.lowercaseString.containsString(searchText.lowercaseString) })
Let's take a look at the definition of cockpitDict
on line 12:
private var cockpitDict = [String: [Dictionary]]()
Because this is an Array
with a String
key and an Array<Dictionary>
value, you have both a key
and value
to capture in the closure. The filter statement needs to be different, something like this (again, I've made some assumptions on the definition of your custom Dictionary
type):
dictionaryItems = cockpitDict.filter({(key, value) -> Bool in let categoryMatch = (scope == "All") || (value.category == scope) return categoryMatch && value.name.lowercaseString.containsString(searchText.lowercaseString) })
Answers 2
I went through whole code provided in github and resolved it
Change your viewDidLoad to
override func viewDidLoad() { super.viewDidLoad() // Load menu items from database if let managedObjectContext = (UIApplication.sharedApplication().delegate as? AppDelegate)?.managedObjectContext { let fetchRequest = NSFetchRequest(entityName: "DictionaryEntity") do { dictionaryItems = try managedObjectContext.executeFetchRequest(fetchRequest) as! [Dictionary] } catch { print("Failed to retrieve record") print(error) } } searchController = UISearchController(searchResultsController: nil) tableView.tableHeaderView = searchController.searchBar searchController.searchResultsUpdater = self searchController.dimsBackgroundDuringPresentation = false searchController.searchBar.placeholder = "Search ..." // you were setting before initialization searchController.searchBar.scopeButtonTitles = ["All", "type", "year", "country"] navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .Plain, target: nil, action: nil) // Enable self sizing cells tableView.estimatedRowHeight = 100.0 tableView.rowHeight = UITableViewAutomaticDimension createCockpitDict() }
and For last scopebar filtering
func filterContentForSearchText(var searchText: String, scope: NSInteger) { searchText = searchText.lowercaseString; func checkString(strData: String, strSearchData: String)-> Bool{ return strData.rangeOfString(strSearchData, options: NSStringCompareOptions.CaseInsensitiveSearch) != nil } searchResults = dictionaryItems.filter({ (dictionary:Dictionary) -> Bool in switch scope { case 0: return (checkString(dictionary.word!, strSearchData: searchText) || checkString(dictionary.type!, strSearchData: searchText) || checkString(dictionary.country!, strSearchData: searchText) || checkString(dictionary.year!, strSearchData: searchText)) case 1: return checkString(dictionary.type!, strSearchData: searchText) case 2: return checkString(dictionary.year!, strSearchData: searchText) case 3: return checkString(dictionary.country!, strSearchData: searchText) default: return true; } }) tableView.reloadData() } // MARK: - UISearchBar Delegate func searchBar(searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) { filterContentForSearchText(searchBar.text!, scope: searchBar.selectedScopeButtonIndex) } // MARK: - UISearchResultsUpdating Delegate - comment older method so duplicate method error will be vanished func updateSearchResultsForSearchController(searchController: UISearchController) { let searchBar = searchController.searchBar filterContentForSearchText(searchController.searchBar.text!, scope: searchBar.selectedScopeButtonIndex) }
0 comments:
Post a Comment