Newbie Questions Newbie Questions - 23 days ago 9
Swift Question

UISearchController Filter Swift

I have a

UITableViewCell
with 3 subviews that I would like to filter when I search, as seen here:

TableViewCell Example

1.The image view
2.The name label (black text)
3.The street name label (blue text)

This is what I've done so far, I've only managed to understand how to filter 1 array which is the name:

MainTableView.swift



var FilteredNames = [String]

func updateSearchResultsForSearchController(searchController:UISearchController) {

// Filter Names
self.filteredNames = self.names.filter { (name:String) -> Bool in
if name.lowercaseString.containsString(self.searchController.searchBar.text!.lowercaseString){
return true
} else {
return false
}
}
self.resultsController.tableView.reloadData()
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 100.5
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

// Search
if tableView == self.tableView {
self.streets.count
return self.names.count
} else {
self.filteredStreets.count
return self.filteredNames.count
}
// return names.count
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{

let cell = self.tableView.dequeueReusableCellWithIdentifier("CustomCell", forIndexPath: indexPath) as! CustomCell

if tableView == self.tableView {
cell.photo.image = self.images[indexPath.row]
cell.name.text = names[indexPath.row]
cell.streetName.text = streets[indexPath.row]
} else {
cell.photo.image = self.images[indexPath.row]
cell.name.text = self.filteredNames[indexPath.row]
cell.streetName.text = self.streets[indexPath.row]
}
return cell
}


As of right now, when I search, the image view & the street label are not synchronized with the name. I want to filter all 3 subviews to synchronize correctly. How can I achieve this?

I understand i need to use a struct and filter all 3 with one object but i'v encountered some difficulties managing to do that any help will be appreciated thank you !

Edit:here is my code right now:



override func viewDidLoad() {
super.viewDidLoad()

var searchController : UISearchController!

var resultsController = UITableViewController()

// //
definesPresentationContext = true
self.resultsController.tableView.dataSource = self
self.resultsController.tableView.delegate = self
self.searchController = UISearchController(searchResultsController: self.resultsController)
self.tableView.tableHeaderView = self.searchController.searchBar
self.searchController.searchResultsUpdater = self
self.searchController.dimsBackgroundDuringPresentation = true
self.searchController.searchBar.sizeToFit()
self.searchController.searchBar.barTintColor = UIColor.blackColor()
self.searchController.searchBar.endEditing(true)
self.searchController.searchBar.placeholder = "חפש ברים"



allUsers = createUsers(names: names, streets: streets, images: images)

filteredUsers = allUsers

}

var allUsers: [User]!
var filteredUsers: [User]!

func createUsers(names names: [String], streets: [String], images: [UIImage?]) -> [User] {
var users = [User]()
guard names.count == streets.count && names.count == images.count else { return users }
for (index, name) in names.enumerate() {
let user = User(name: name, streetName: streets[index], image: images[index])
users.append(user)
}
return users
}




//MARK : Search !
func updateSearchResultsForSearchController(searchController:UISearchController) {
if let searchText = searchController.searchBar.text?.lowercaseString {
if searchText.characters.count == 0 {
filteredUsers = allUsers
}
else {
filteredUsers = allUsers.filter {
return $0.name.lowercaseString.containsString(searchText) ||
$0.streetName.lowercaseString.containsString(searchText)
}
}
}
self.resultsController.tableView.reloadData()
}

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
{
return 100.5;
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
////////

////////
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredUsers.count
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let cell = self.tableView.dequeueReusableCellWithIdentifier("CustomCell", forIndexPath: indexPath) as! CustomCell
let user = filteredUsers[indexPath.row]
cell.photo.image = user.image
cell.name.text = user.name
cell.streetName.text = user.streetName
return cell
}

Answer

I would strongly recommend to have only one dataSource instead of separate arrays for names, street names and images. In the following code filteredUsers is always used as dataSource and allUsers is just a stored full array, which is used for updating filteredUsers every time a new text is entered in search bar.

Create a model:

struct User {
    var name: String
    var streetName: String
    var image: UIImage?
}

ViewController:

var allUsers: [User]!
var filteredUsers: [User]!

override func viewDidLoad() {
    super.viewDidLoad()

    //assuming you already have three arrays with the same amount of elements in each:
    allUsers = createUsers(names: names, streets: streets, images: images)

    filteredUsers = allUsers
}

func createUsers(names names: [String], streets: [String], images: [UIImage?]) -> [User] {
    var users = [User]()
    guard names.count == streets.count && names.count == images.count else { return users }
    for (index, name) in names.enumerate() {
        let user = User(name: name, streetName: streets[index], image: images[index])
        users.append(user)
    }
    return users
}

func  updateSearchResultsForSearchController(searchController:UISearchController) {
    if let searchText = searchController.searchBar.text?.lowercaseString {
        if searchText.characters.count == 0 {
            filteredUsers = allUsers
        }
        else {
            filteredUsers = allUsers.filter {
                return $0.name.lowercaseString.containsString(searchText) || 
                       $0.streetName.lowercaseString.containsString(searchText)
            } ?? []
        }            
    }
    self.resultsController.tableView.reloadData()
}


override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
     return filteredUsers ? filteredUsers.count : 0
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
    let cell = self.tableView.dequeueReusableCellWithIdentifier("CustomCell", forIndexPath: indexPath) as! CustomCell 
    let user = filteredUsers[indexPath.row]
    cell.photo.image = user.image
    cell.name.text = user.name
    cell.streetName.text = user.streetName
    return cell
}
Comments