iosswiftuicollectionviewuisearchbar

UICollectionView not responding to UISearchBar


I have created a UICollectionView containing data from an API, which displays titles and poster images. The UICollection works fine showing both correctly.

I am trying to implement a UISearchBar that searches based on the title. The issue I am having is the UICollectionView does not respond or update when using the UISearchBar.

Here is my current code:

import UIKit
import AFNetworking

class FilmsViewController: UIViewController, UICollectionViewDelegate, 
UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, 
UISearchBarDelegate, UISearchControllerDelegate {

var films: [NSDictionary]?
var filteredFilms: [NSDictionary]?
var searching:Bool = false



override func viewDidLoad() {
    super.viewDidLoad()
    
    filmsTable.dataSource = self
    filmsTable.delegate = self
    
    loadFilms()
    
}

override func viewDidAppear(_ animated: Bool) {
        
    navigationItem.titleView = imageView
}

//SEARCH BAR
   
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
    
    self.filteredFilms?.removeAll()
    if searchText != "" {
        searching = true
        for film in self.films! {
            if (film["title"] as! String).contains(searchText.lowercased()) {
                self.filteredFilms?.insert(film, at: (filteredFilms?.endIndex)!)
            }
        }
    } else {
        searching = false 
        self.filteredFilms = self.films
    }
    
    filmsTable.reloadData()
}

/*
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
    
    let search = searchBar.text!
    
    if search.isEmpty {
        self.filmsTable.reloadData()
    }
    else {
        
        // let search = searchBar.text!
        
        filteredFilms = films!.filter({ (text) -> Bool in
            
            //Access the title and sectors
            let filmTitle = text["title"] as! NSString
            
            //Create a range for both
            let range1 = filmTitle.range(of: search, options: NSString.CompareOptions.caseInsensitive)
           
            self.filmsTable.reloadData()
            return range1 != nil
            
        })
        
        self.filmsTable.reloadData()
       
    }
    
}

*/

//Collection View Layout

func collectionView(_ _collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    
    if searching {
        return filteredFilms?.count ?? 0
    } else {
        return films?.count ?? 0
    }
}

    
 
      
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
        let cell = filmsTable.dequeueReusableCell(withReuseIdentifier: "filmCell", for: indexPath) as! FilmCell
        
        if searching {
            let film = filteredFilms![indexPath.row]
            let title = film["title"] as! String
            cell.titleLabel.text = title
            
        } else {
            let film = films![indexPath.row]
            let title = film["title"] as! String
           
            cell.titleLabel.text = title

        }
        
        return cell
    }
    
    //End Collection View Layout
    
    //Parse Film API
    
    func loadFilms() {
        
        let apiKey = ""
        let url = URL(string: "https://api.themoviedb.org/3/movie/now_playing?api_key=\(apiKey)&language=en-US&page=1")
        let request = URLRequest(
            url: url! as URL,
            cachePolicy: URLRequest.CachePolicy.reloadIgnoringLocalCacheData,
            timeoutInterval: 10 )
        
        let session = URLSession (
            configuration: URLSessionConfiguration.default,
            delegate: nil,
            delegateQueue: OperationQueue.main
        )
        
        let task = session.dataTask(with: request, completionHandler: { (dataOrNil, response, error) in
            if let data = dataOrNil {
                if let resposeDictionary = try! JSONSerialization.jsonObject(with: data, options:[]) as? NSDictionary {
                    
                    self.films = resposeDictionary["results"] as? [NSDictionary]
                    print("response: \(resposeDictionary)")
                }
                
            }
            
            self.filteredFilms = self.films
            self.filmsTable.reloadData()
            
        })
        
        task.resume()
        
    }
    
    //End Parse Film API



}

EDIT: I've also tried this. I get error at the "filmTable =" line:

"Cannot subscript a value of type '[NSDictionary]' with an index of type 'String' .

func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
    if searchBar.text == nil || searchBar.text ==  "" {
        searching = false
        filmsTable.reloadData()
    } else {
        searching = true
        let filmTitle = films!["title"] as! NSString
        filteredFilms = films!.filter({$0.filmTitle.range(of: searchBar.text!) != nil})
        filmsTable.reloadData()
    }
}

Solution

  • Not sure where exactly in your code you are having trouble. However, I can give you a few suggestions and a working example of what you are trying to do.

    1. If you are trying to populate a collectionView using an array always declare the arrays as empty arrays (in case you need it empty) instead of optionals.
    2. If you don't need a boolean, don't use it - in your case the searching boolean is not needed to be honest.
    3. Simplify as much as possible your code. In your case, the filter function will give you a filtered array - So no need to insert or append items in the filteredFilms array.

    Here is a working example of what you want to do:

    class ViewController: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDelegate, UICollectionViewDataSource, UISearchBarDelegate {
    
        lazy var collectionView : UICollectionView = {
            let layout = UICollectionViewFlowLayout()
            let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
            cv.backgroundColor = .white
            cv.dataSource = self
            cv.delegate = self
            return cv
        }()
    
        var films = [["title" : "one"], ["title" : "two"], ["title" : "three"]]
        var filteredFilms = [Dictionary<String, String>]()
    
        let searchBar = UISearchBar()
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            self.view.addSubview(searchBar)
            searchBar.translatesAutoresizingMaskIntoConstraints = false
            searchBar.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 60).isActive = true
            searchBar.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
            searchBar.heightAnchor.constraint(equalToConstant: 60).isActive = true
            searchBar.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
    
            self.view.addSubview(collectionView)
            collectionView.translatesAutoresizingMaskIntoConstraints = false
            collectionView.topAnchor.constraint(equalTo: self.searchBar.bottomAnchor).isActive = true
            collectionView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
            collectionView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
            collectionView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
    
            collectionView.register(Cell.self, forCellWithReuseIdentifier: "cell")
    
            self.searchBar.delegate = self
            self.filteredFilms = films
        }
    
        func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            return filteredFilms.count
        }
    
        func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
            return cell
        }
    
        func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
            return CGSize(width: 100, height: 100)
        }
    
        func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
            if searchText == "" {
                self.filteredFilms = films
                self.collectionView.reloadData()
            } else {
                self.filteredFilms = films.filter({($0["title"]?.contains(searchText.lowercased()))!})
                print(self.filteredFilms.count)
                self.collectionView.reloadData()
            }
        }
    
    }
    
    class Cell : UICollectionViewCell {
        override init(frame: CGRect) {
            super.init(frame: frame)
            setupViews()
        }
    
        func setupViews() {
            self.backgroundColor = .red
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    }