Swift: Have SearchBar search through both sections

2019-05-26 11:36发布

问题:

Using Swift 3, the app is a blog reader reading from a MYSQL database using PHP and JSON.

Currently my SearchBar is not doing what I want, I have it searching in my mainArray (section 1) with the 'All' scope. As it's being filtered the objects that are being filtered are moved to filteredArray. I did this in the mean time because I can not figure out how to make it do what I want.

What it's supposed to do is this, when the user is searching for an object, I want the object to show up wether it's in mainArray or followedArray and not move it to a different array so its doesn't combine. Basically filter the tableview and not remove any sections or combine any objects as it will confuse the user by not knowing which section the object was in, and of course make sure the scope bar is working properly.

Learning how to implement a search bar so you can see my trouble when I try to take it a level further. Thank you!

SearchBar & Scope code

let searchController = UISearchController(searchResultsController: nil)

override func viewDidLoad() {
    // Search Bar
    searchController.searchResultsUpdater = self
    searchController.dimsBackgroundDuringPresentation = false
    definesPresentationContext = true
    myTableView.tableHeaderView = searchController.searchBar
    searchController.searchBar.backgroundColor = UIColor.white
    searchController.searchBar.barTintColor = UIColor.white

    // Scope Bar
    searchController.searchBar.scopeButtonTitles = ["All", "Released", "Unreleased", "Free"]
    searchController.searchBar.delegate = self
}

// Title for Header
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {

    if !(searchController.isActive && searchController.searchBar.text != "") {

        if section == 0 {
            return "Followed Blogs"
        }
        else {
            return "All Blogs"
        }
    }
    return "Filtered Blogs"
}

// Number of Rows in Section
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

    if !(searchController.isActive && searchController.searchBar.text != "") {

        if section == 0 {

            return followedArray.count
        }
        else if (section == 1) {

            return mainArray.count
        }
    }
    return filteredArray.count
}

// Number of Sections
func numberOfSections(in tableView: UITableView) -> Int {

    if !(searchController.isActive && searchController.searchBar.text != "") {

        return 2
    }
    return 1
}

// CellForRowAt indexPath
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let CellIdentifier = "Cell"
    var cell = tableView.dequeueReusableCell(withIdentifier: CellIdentifier) as! CustomCell

    if cell != cell {
        cell = CustomCell(style: UITableViewCellStyle.default, reuseIdentifier: CellIdentifier)
    }

    // Configuring the cell
    var blogObject: Blog

    if !(searchController.isActive && searchController.searchBar.text != "") {
        if indexPath.section == 0 {
            blogObject = followedArray[indexPath.row] 
            cell.populateCell(blogObject, isFollowed: true, indexPath: indexPath, parentView: self)
        }
        else if indexPath.section == 1 {
            blogObject = mainArray[indexPath.row] 
            cell.populateCell(blogObject, isFollowed: false, indexPath: indexPath, parentView: self)
        }
    }
    else {
        blogObject = filteredArray[indexPath.row] 
        cell.populateCell(blogObject, isFollowed: false, indexPath: indexPath, parentView: self)
    }

    return cell
}

// SEARCH BAR: Filtering Content
func filterContentForSearchText(searchText: String, scope: String = "All") {

    filteredArray = mainArray.filter { Blog in

        let categoryMatch = (scope == "All") || (Blog.blogType == scope)

        return categoryMatch && (Blog.blogName.lowercased().contains(searchText.lowercased()))
    }
    myTableView.reloadData()
}

// SEARCH BAR: Updating Results
func updateSearchResults(for searchController: UISearchController) {

    filterContentForSearchText(searchText: searchController.searchBar.text!)
}

// SEARCH BAR: Scope
func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {

    filterContentForSearchText(searchText: searchBar.text!, scope: searchBar.scopeButtonTitles![selectedScope])
}

// SEARCH BAR: Updating Scope
func updateSearchResultsForSearchController(searchController: UISearchController) {

    let searchBar = searchController.searchBar
    let scope = searchBar.scopeButtonTitles![searchBar.selectedScopeButtonIndex]
    filterContentForSearchText(searchText: searchController.searchBar.text!, scope: scope)
}

// Deallocating Search Bar
deinit{
    if let superView = searchController.view.superview {
        superView.removeFromSuperview()
    }
}

回答1:

Right now you create a single array (filteredArray) and assume you have 1 section when searching.

I would remove that assumption.

Inside your filterContentForSearchText method, create an array of arrays (where each inner array represents a section).

Then update all of your table view data source methods to work with that array of arrays to get the proper values.

First, update your declaration of filteredArray to be an array of arrays.

Now update your table view methods:

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if !(searchController.isActive && searchController.searchBar.text != "") {
        if section == 0 {
            return followedArray.count
        } else {
            return mainArray.count
        }
    } else {
        return filteredArray[section].count
    }
}

// Number of Sections
func numberOfSections(in tableView: UITableView) -> Int {
    if !(searchController.isActive && searchController.searchBar.text != "") {
        return 2
    } else {
        return filteredArray.count
    }
}

// CellForRowAt indexPath
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let CellIdentifier = "Cell"
    var cell = tableView.dequeueReusableCell(withIdentifier: CellIdentifier) as! CustomCell

    if cell != cell {
        cell = CustomCell(style: UITableViewCellStyle.default, reuseIdentifier: CellIdentifier)
    }

    // Configuring the cell
    var blogObject: Blog

    if !(searchController.isActive && searchController.searchBar.text != "") {
        if indexPath.section == 0 {
            blogObject = followedArray[indexPath.row] 
            cell.populateCell(blogObject, isFollowed: true, indexPath: indexPath, parentView: self)
        } else {
            blogObject = mainArray[indexPath.row] 
            cell.populateCell(blogObject, isFollowed: false, indexPath: indexPath, parentView: self)
        }
    } else {
        blogObject = filteredArray[indexPath.section][indexPath.row] 
        cell.populateCell(blogObject, isFollowed: false, indexPath: indexPath, parentView: self)
    }

    return cell
}

And finally update the filterContentForSearchText method:

func filterContentForSearchText(searchText: String, scope: String = "All") {
    let filteredFollowed = followedArray.filter { Blog in
        let categoryMatch = (scope == "All") || (Blog.blogType == scope)

        return categoryMatch && (Blog.blogName.lowercased().contains(searchText.lowercased()))
    }

    let filteredMain = mainArray.filter { Blog in
        let categoryMatch = (scope == "All") || (Blog.blogType == scope)

        return categoryMatch && (Blog.blogName.lowercased().contains(searchText.lowercased()))
    }

    filteredArray = [ filteredFollowed, filteredMain ]

    myTableView.reloadData()
}