在swift中添加Scope Bar

时间:2016-02-29 15:08:05

标签: ios swift uisearchbar

该应用程序显示了在带有搜索栏的tableview中工作的汽车零件(汽车零件,类型,年份,国家)的列表。

现在我决定添加一个范围栏来过滤结果,我确信我搞砸了代码。添加scopeBar的行中有许多错误。在评论//ScopeBar try之后的最后20行,除了最后一行之外,我在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'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 个答案:

答案 0 :(得分:4)

我浏览了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)
}

答案 1 :(得分:3)

此代码存在一些问题。对于初学者,正如其他人所指出的那样,var searchResults:[Dictionary] = []不是有效的语法。 Swift中的Dictionary需要类型规范,例如Dictionary<String: AnyObject>。我猜测你的代码的其余部分已经命名为一个名为Dictionary的自定义实体,这可能会引起混淆。

我看到的下一个问题是,在第8行和第10行,您需要使用()初始化数组,即var searchResults:[Dictionary] = []()。 (同样,这是假设您正确解决了正确声明Dictionary类型的问题。)

另一个问题是第192行的filter声明:

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

让我们看一下第12行cockpitDict的定义:

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

由于这是一个Array,其String键和Array<Dictionary>值,因此您需要在闭包中捕获keyvalue。 filter语句需要不同,类似这样(再次,我对自定义Dictionary类型的定义做了一些假设):

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