Friday, March 11, 2016

Adding Scope Bar in swift

Leave a Comment

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) } 
If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment