Reputation: 35
So I'm trying to add a SearchBar on top of my TableView so when someone searches something It will show that cell, I did some research, and all of the examples were using an array[String] and my array has a data model. If anyone could help me out with this. Thank you
Data Model
struct SurahData: Decodable {
let data: [Datum]
}
struct Datum: Decodable {
let number: Int
let englishName,englishNameTranslation: String
}
View Controller, Each Cell has two labels
import UIKit
class SecondViewController: UIViewController {
var activityIndicator:UIActivityIndicatorView = UIActivityIndicatorView()
@IBOutlet weak var searchBar: UISearchBar!
@IBOutlet weak var tableView: UITableView!
var emptyArray = [Datum]()
var numberArray = [Int](1...114)
let gradientLayer = CAGradientLayer()
override func viewDidLoad() {
super.viewDidLoad()
startIndicator()
activityIndicator.startAnimating()
activityIndicator.backgroundColor = .white
callingSurah()
gradientLayer.frame = view.bounds
gradientLayer.colors = [
UIColor.systemRed.cgColor,
UIColor.systemOrange.cgColor,
]
}
override func viewWillLayoutSubviews() {
gradientLayer.frame = CGRect(x: 0, y: 0, width: view.bounds.width, height: view.bounds.height)
}
func startIndicator() {
activityIndicator = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
activityIndicator.style = UIActivityIndicatorView.Style.large
activityIndicator.center = self.view.center
self.view.addSubview(activityIndicator)
}
func callingSurah(){
let quranURL = "https://api.alquran.cloud/v1/surah"
guard let convertedURL = URL(string: quranURL) else {return}
URLSession.shared.dataTask(with: convertedURL) { data, response, error in
if let error = error {
self.activityIndicator.stopAnimating()
self.activityIndicator.hidesWhenStopped = true
print("An error has occured while performing the call \(String(describing: error))")
}
if let data = data {
do{
let decoder = try JSONDecoder().decode(SurahData.self, from: data)
DispatchQueue.main.async{
self.activityIndicator.stopAnimating()
self.activityIndicator.hidesWhenStopped = true
self.emptyArray = decoder.data
self.tableView.reloadData()
print(self.emptyArray)
}
}
catch{
self.activityIndicator.stopAnimating()
self.activityIndicator.hidesWhenStopped = true
print("Error occured while decoding \(String(describing: error))")
}
}
}
.resume()
}
}
extension SecondViewController: UITableViewDelegate, UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return emptyArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "SurahsViewCell", for: indexPath) as? SurahsViewCell else {return
UITableViewCell()}
cell.englishName.text = emptyArray[indexPath.row].englishName
cell.engTrans.text = emptyArray[indexPath.row].englishNameTranslation
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let vc = storyboard?.instantiateViewController(withIdentifier: "AyahController") as? AyahController else {return}
vc.surahNumber = emptyArray[indexPath.row].number
navigationController?.pushViewController(vc, animated: true)
}
}
Upvotes: 1
Views: 34
Reputation: 71854
You need to handle search text with UISearchBarDelegate
Declare an Array
which can store your search results:
var filteredArr: [Datum]?
Declare Bool
to keep track if search is active:
var isSearchActive = false
Assign delegate in your viewDidLoad
method
searchBar.delegate = self
Add delegate methods
extension ViewController: UISearchBarDelegate {
func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool {
isSearchActive = true //Make search active
self.tableView.reloadData()
return true
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
guard let surahList = self.emptyArray else { return }
self.filteredArr = searchText.isEmpty ? surahList : surahList.filter {
($0.englishName).range(of: searchText, options: .caseInsensitive) != nil //Filter data with user input for englishName
}
self.tableView.reloadData()
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
isSearchActive = false //deactivate search
self.tableView.reloadData()
}
}
And you need to update your tableView
delegate methods as well
extension ViewController: UITableViewDelegate, UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isSearchActive {
return filteredArr?.count ?? 0
} else {
return emptyArray?.count ?? 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "SurahsViewCell", for: indexPath) as? SurahsViewCell else {return
UITableViewCell()}
var product: Datum?
if isSearchActive {
product = self.filteredArr?[indexPath.row]
} else {
product = self.emptyArray?[indexPath.row]
}
cell.englishName.text = product?.englishName ?? ""
cell.engTrans.text = product?.englishNameTranslation ?? ""
return cell
}
}
Here is the full code and I made some minor update as well:
class ViewController: UIViewController {
var activityIndicator:UIActivityIndicatorView = UIActivityIndicatorView()
@IBOutlet weak var searchBar: UISearchBar!
@IBOutlet weak var tableView: UITableView!
var emptyArray: [Datum]? //Declare it as optional
let gradientLayer = CAGradientLayer()
var filteredArr: [Datum]?
var isSearchActive = false
override func viewDidLoad() {
super.viewDidLoad()
startIndicator()
activityIndicator.startAnimating()
activityIndicator.backgroundColor = .white
callingSurah()
gradientLayer.frame = view.bounds
gradientLayer.colors = [
UIColor.systemRed.cgColor,
UIColor.systemOrange.cgColor,
]
searchBar.delegate = self
}
override func viewWillLayoutSubviews() {
gradientLayer.frame = CGRect(x: 0, y: 0, width: view.bounds.width, height: view.bounds.height)
}
func startIndicator() {
activityIndicator = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
activityIndicator.style = UIActivityIndicatorView.Style.large
activityIndicator.center = self.view.center
self.view.addSubview(activityIndicator)
}
func callingSurah(){
let quranURL = "https://api.alquran.cloud/v1/surah"
guard let convertedURL = URL(string: quranURL) else {return}
URLSession.shared.dataTask(with: convertedURL) { data, response, error in
if let error = error {
self.activityIndicator.stopAnimating()
self.activityIndicator.hidesWhenStopped = true
print("An error has occured while performing the call \(String(describing: error))")
}
if let data = data {
do{
let decoder = try JSONDecoder().decode(SurahData.self, from: data)
DispatchQueue.main.async{
self.activityIndicator.stopAnimating()
self.activityIndicator.hidesWhenStopped = true
self.emptyArray = decoder.data
self.tableView.reloadData()
}
}
catch{
self.activityIndicator.stopAnimating()
self.activityIndicator.hidesWhenStopped = true
print("Error occured while decoding \(String(describing: error))")
}
}
}
.resume()
}
}
extension ViewController: UISearchBarDelegate {
func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool {
isSearchActive = true
self.tableView.reloadData()
return true
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
guard let surahList = self.emptyArray else { return }
self.filteredArr = searchText.isEmpty ? surahList : surahList.filter {
($0.englishName).range(of: searchText, options: .caseInsensitive) != nil
}
self.tableView.reloadData()
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
isSearchActive = false
self.tableView.reloadData()
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isSearchActive {
return filteredArr?.count ?? 0
} else {
return emptyArray?.count ?? 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "SurahsViewCell", for: indexPath) as? SurahsViewCell else {return
UITableViewCell()}
var product: Datum?
if isSearchActive {
product = self.filteredArr?[indexPath.row]
} else {
product = self.emptyArray?[indexPath.row]
}
cell.englishName.text = product?.englishName ?? ""
cell.engTrans.text = product?.englishNameTranslation ?? ""
return cell
}
}
Upvotes: 2