Добавление области видимости в быстром

Приложение отображает список частей автомобилей (часть автомобиля, тип, год, страна), он работает в виде таблицы с панелью поиска.

Теперь я решил добавить панель видимости для фильтрации результатов, и я уверен, что я испортил код. В строках добавлено много ошибок, чтобы добавить scopeBar. Это последние 20 строк, после комментария //ScopeBar try, кроме этих последних строк, я добавил код в viewDidLoad(), чтобы показать названия, которые я хочу.

Что я делаю неправильно? Любая помощь более чем приветствуется, я пытаюсь исправить это в течение 2 дней уже не повезло.

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 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)
    }
}

Ответ 1

Я прошел весь код, предоставленный в github, и разрешил его

Измените свой viewDidLoad на

 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()
    }

и для фильтрации последней области видимости

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)
}

Ответ 2

Есть несколько проблем с этим кодом. Для начала, как указывали другие, var searchResults:[Dictionary] = [] недействителен синтаксис. A Dictionary в Swift требует спецификации типа, например Dictionary<String: AnyObject>. Я предполагаю, основываясь на остальной части вашего кода, что вы назвали пользовательский объект с именем Dictionary, который может вызвать путаницу.

Следующая проблема, которую я вижу, это то, что в строках 8 и 10 вам нужно инициализировать массив, используя (), т.е. var searchResults:[Dictionary] = [](). (Опять же, предполагается, что вы правильно исправите проблему объявления своего типа Dictionary.)

Другая проблема - ваш оператор filter в строке 192:

dictionaryItems = cockpitDict.filter({( cockpitDict : Dictionary) -> Bool in
    let categoryMatch = (scope == "All") || (cockpitDict.category == scope)
    return categoryMatch && cockpitDict.name.lowercaseString.containsString(searchText.lowercaseString)
})

Посмотрим на определение cockpitDict в строке 12:

private var cockpitDict = [String: [Dictionary]]()

Поскольку это Array с ключом String и значением Array<Dictionary>, у вас есть как key, так и value для захвата в закрытии. Оператор фильтра должен быть другим, что-то вроде этого (опять же, я сделал некоторые предположения относительно определения вашего пользовательского типа Dictionary):

dictionaryItems = cockpitDict.filter({(key, value) -> Bool in
    let categoryMatch = (scope == "All") || (value.category == scope)
    return categoryMatch && value.name.lowercaseString.containsString(searchText.lowercaseString)
})