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