Adding element to UITableView - ios

I am trying to add an element into the array cart.
After executing this function more than once on different items, I see from the print statement that the count never increases and always stay at 1. Furthermore, the new element replaces the old element rather than appending to the array.
Additionally, I don't know how to display the data into the table view of cartTableViewController. Whenever I navigate to cartTableViewController, no elements show up.
#IBAction func addToCart(_ sender: UIBarButtonItem) {
let mainStoryBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
if let cartTableViewController = mainStoryBoard.instantiateViewController(withIdentifier: "CartTableView") as? CartTableViewController {
var cart = cartTableViewController.cartItems
let itemToAdd = CartItem(product: product!, quantity: quantity)
print(itemToAdd!.product.itemName) // the new item replaces the existing one
cart.append(itemToAdd!)
print(cart.count) // count is always 1
print(quantityLabel.text! + " " + (product?.itemName)! + " added")
}
}

Try this may help you,
var cart: [CartItem]!
override func viewDidLoad() {
super.viewDidLoad()
let mainStoryBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
if let cartTableViewController = mainStoryBoard.instantiateViewController(withIdentifier: "CartTableView") as? CartTableViewController {
cart = cartTableViewController.cartItems
}
}
#IBAction func addToCart(_ sender: UIBarButtonItem) {
if cart != nil {
let itemToAdd = CartItem(product: product!, quantity: quantity)
print(itemToAdd!.product.itemName) // the new item replaces the existing one
cart.append(itemToAdd!)
print(cart.count) // count is always 1
print(quantityLabel.text! + " " + (product?.itemName)! + " added")
}
}

I can clearly say that you have a problem with the below line because the value of cartTableViewController.cartItems always nil or empty
let cartTableViewController = mainStoryBoard.instantiateViewController(withIdentifier: "CartTableView") as? CartTableViewController
var cart = cartTableViewController.cartItems
Every time, You have to use old instead of the initialized new object.
You can fix your problem defining a global array of the cart which is
accessible throughout app.
Code:
let cartTableViewController = mainStoryBoard.instantiateViewController(withIdentifier: "CartTableView") as? CartTableViewController
var cart = aryCart //Your cart global object
print(cart.count) // Updated Count
Update:
Just define a variable outside of class then it will automatically accessible throughout application.
var aryCart = [CartItem]()
class CartVC:UIViewController{
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func addToCart(_ sender: UIBarButtonItem) {
let mainStoryBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
if let cartTableViewController = mainStoryBoard.instantiateViewController(withIdentifier: "CartTableView") as? CartTableViewController {
let itemToAdd = CartItem(product: product!, quantity: quantity)
print(itemToAdd!.product.itemName) // the new item replaces the existing one
aryCart.append(itemToAdd!)
print(aryCart.count) // count is always 1
print(quantityLabel.text! + " " + (product?.itemName)! + " added")
}
}
}

The new array you're creating only exists within the scope of the function. When assigning cart to cartTableViewController.cartItems, this only copies the values from cartItems into cart, it doesn't assign the pointer. For example:
var cartItems = ["First element"]
var cart = cartItems
cart.append("Second Element")
print(cartItems)
print(cart)
This would print:
["First element"]
["First element", "Second Element"]
It looks like you're adding data to an array in another ViewController. I would generally advise you to update state through some other mechanism than the instance variable in the other view controller. There are many philosophies about how to structure code in the best way, one solution for you could be to use the MVVM pattern. This article covers it: https://medium.com/#azamsharp/mvvm-in-ios-from-net-perspective-580eb7f4f129
A quick way to "clean up" a little bit would be to have an intermediate object where you store the cartItems data, which you have a reference to in both the current view controller and CartTableView, such as CartItemStore:
class CartItemStore {
static let shared = CartItemStore()
private let cartItems: [CartItem]
// you can add logic for reading previously added cart items from cache in order to persist it.
// The same goes when adding new cartItems
private init() {
self.cartItems = []
}
func getItem(_ index: Int) -> CartItem? {
//Check whether the index passed in is valid
if cartItems.indices.contains(index) {
return cartItems[index]
} else {
return nil
}
}
func addItem(_ item: CartItem) {
cartItems.append(item)
}
func numberOfItems() -> Int {
return cartItems.count
}
}
Then you can use your singleton store in the following way:
// This inside the current view controller
private let cartItemStore = CartItemStore.shared
#IBAction func addToCart(_ sender: UIBarButtonItem) {
...
let itemToAdd = CartItem(product: product!, quantity: quantity)
cartItemStore.addItem(itemToAdd)
print(cartItemStore.numberOfItems())
}
Then you can read items from the other view controller as well by accessing the singleton.
Singletons should generally be avoided since it can be hard to test your application. Singletons allow state to be updated wherever, which can create a nightmare if you abuse it good enough. I used it here to give you a quick way to accomplish something that is a little less coupled, but if you will sit with this code base for a while you might want to rethink this. Feel free to look into MVVM and how to manage state with it, it will make things easier, in the long run, to have a structured way of managing state in your application :) Hope this is to any help of you.

Related

How to iterate through a dictionary, loading the next item with button press in swift

I am making an app that will ask a series of questions, giving a value when answering yes or no. I have an initial view controller with a collection view of question types. This view controller passes the type information to a new view controller with the question layout (image, label, buttons). On the second view controller I have a switch/case statement to provide the proper dictionary results for the type that was passed in (via questionTypeReceived), populating the initial view correctly. Once you answer the question (simple yes or no) I want to update with the next question. Any advice to get this done?
Extra info: I have the questions view controller (referred to here as secondary VC) refactored because I initially thought I wanted to reuse it, not just reload it. All aspects work EXCEPT the reloading with the next question... Yes, that is because I don't have the code in there to do that. I have been trying different for loops in different places but keep getting errors so I go back to the current setup because there are no errors
Initial VC
let names = [ "Work", "School", "Family", "Friends", "Random" ]
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
questionType = names[indexPath.row] as String
let storyboard:UIStoryboard = UIStoryboard(name: "questions", bundle: nil)
let questionsVC = storyboard.instantiateViewController(withIdentifier: "questionsViewController") as! questionsViewController
questionsVC.questionTypeReceived = questionType
self.navigationController?.pushViewController(questionsVC, animated: true)
performSegue(withIdentifier: "question_segue", sender: questionType)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let dest = segue.destination as? questionsViewController else {
print("Can't perform downcast")
return
}
dest.questionTypeReceived = questionType
}
secondary VC
var questionTypeReceived:String = String()
var answerTotalValue:Double = Double()
var questionNumber:Int = Int()
var questionBody = ""
var questionType = ""
let answerYesValue:Double = Double.random(in: 1...5)
let answerNoValue:Double = Double.random(in: 0..<3)
switch questionTypeReceived {
case "Random":
questionTypeReceived = "Random"
questionsImageView.image = UIImage(named: randomImages.randomElement()!)
let questionNumber = randomQuestions.keys.sorted()
let questionBody = randomQuestions.values
for (questionNumber, questionBody) in randomQuestions {
questionTitleLabel.text = "Question #\(questionNumber)"
questionsTextLabel.text = questionBody
}
default: return
}
let randomQuestions: [Int : String] = [
1: "Did you already agree to attend?",
2: "Does it cost money to attend?",
3: "Is it time consuming (4+ hours)?"
]
#IBAction func yesButtonPressed(_ sender: UIButton) {
answerTotalValue += answerYesValue
print(answerTotalValue)
print(questionTypeReceived)
}
There seems to be some confusion on how to handle properties and local variables, you have for instance 1 property and 2 local variables named questionNumber and you are also iterating the whole dictionary when as I see it you only want one question.
One way to get away from this and a solution that is also good for other reason like not doing everything in your controller class and separating responsibilities is to move the questions and handling of them into a separate struct or class.
Let's create a Questions struct that holds the questions, keeps track of what questions has been asked and can deliver the next question to ask.
struct Questions {
private randomQuestions: [Int : String] = [
1: "Did you already agree to attend?",
2: "Does it cost money to attend?",
3: "Is it time consuming (4+ hours)?"
]
private var questionIndex = 0
mutating func nextQuestion() -> (number: Int, question: String?) {
questionIndex += 1
return (questionIndex, randomQuestions[questionIndex])
}
}
Note the question mark for the string in the return tuple, if no questions exists for a index (questionIndex > 3) the question returned will be nil
Then you can use it in you code like this, first declare it as a property in your VC
var questions = Questions()
Then when getting the question
switch questionTypeReceived {
case "Random":
questionsImageView.image = UIImage(named: randomImages.randomElement()!)
let nextQuestion = question.nextQuestion()
if let question = nextQuestion.question {
questionTitleLabel.text = "Question #\(nextQuestion.number)"
questionsTextLabel.text = question
} else {
// no more questions to ask, this needs to be handled
}
default: return
}
So a little more work to write a new struct but your code in the VC becomes simpler and a lot of properties and variables are no longer needed.

Getting the value of a constant in another controller is always returning nil using Swift

In an attempt to get the value of historyRef in another VC, it is returning nil. I have tried different solutions (including this one I am using) and I can't seem to get the actual value of the historyRef variable as declared in viewDidLoad().
The Firebase database has a node "history", which has a key (childByAutoId()) in MainVC. I am trying to access that key in SecondVC.
In the MainVC is a constant:
var historyRef : FIRDatabaseReference!
var ref : FIRDatabaseReference!
Also instance is declared:
private static let _instance = MainVC()
static var instance: MainVC {
return _instance
}
viewDidLoad() :
historyRef = ref.child("history").childByAutoId()
SecondVC
class SecondVC: UIViewController {
var mainVC : MainVC? = nil // hold reference
override func viewDidLoad() {
super.viewDidLoad()
mainVC = MainVC() // create MainVC instance
getUserHistoryIds()
}
func getUserHistoryIds() {
let historyKey = mainVC?.historyRef
print("HistoryKey: \(String(describing: historyKey))")
}
}
printout:
HistoryKey: nil
My database:
my-app
- history
+ LSciQTJwR0VqwaAfKVz
Edit
Rather than get from another controller, I got from Firebase.
I was able to get the value of the childAutoById but it lists all of them and not just the current one:
let historyRef = ref.child("history")
historyRef.observe(.value) { (snapshot) in
if snapshot.exists() {
for history in snapshot.children {
let snap = history as! FIRDataSnapshot
let _ = snap.value as! [String: Any] // dict
let historyKey = snap.key
print("History Key: \(historyKey)")
}
} else {
print("There are none")
}
}
You are initialising MainVC::historyRef in the viewDidLoad method, but just instantiating MainVC from SecondVC will not cause the MainVC to be loaded or displayed.
You can use mainVC = MainVC.instance, but you are dependent on the MainVC instance having been previously loaded and not otherwise discarded.
I'd be looking to extract any model usages away from being related to a VC, and passed to the VC as part of a segue when they are required.
Move the history ref from the viewDidLoad to init() when you initialize the MainVC. This way the method will run as soon as you instantiate the view controller. Also make sure that this is not an async call, or add a completion callback to know when it is done, as you mention you are using firebase.
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
historyRef = ref.child("history").childByAutoId()
}
As you instantiate the MainVC but never load it so that historyRef will never run.
Also having a view controller as a static is not recommended. I would rather pass the variable down through initializers or through view models.
As you can see from my edit, I wasn't able to get what I needed from passing between controllers, so I just got it from firebase database:
let historyRef = ref.child("history")
historyRef.observe(.value) { (snapshot) in
if snapshot.exists() {
for history in snapshot.children {
let snap = history as! FIRDataSnapshot
let _ = snap.value as! [String: Any] // dict
let historyKey = snap.key
print("History Key: \(historyKey)")
}
} else {
print("There are none")
}
}
This gets all the random keys rather than the current one, this I can work with.

How to send multiple variables through segue

How can I send multiple variables through a segue in Swift? The QBBust gets sent over fine and prints on the view controller, but the QBName doesn't get sent over for some reason. Can anyone spot why?
if let send = sender as? Double{
destination.QBBust = send
}
if let sent = sender as? String{
destination.QBName = sent
}
}
}
private var _QBName:String!
var QBName: String{
get{
return _QBName
} set {
_QBName = newValue
}
}
private var _QBBust:Double!
var QBBust: Double {
get {
return _QBBust
} set{
_QBBust = newValue
}
}
override func viewDidLoad() {
super.viewDidLoad()
let bust = String(Int(_QBBust))
QBBustLabel.text = "\(bust)%"
QBNameLabel.text = _QBName
}
This next part is in the button function that triggers the segue
performSegue(withIdentifier: "QBResultVC", sender: QBBust)
performSegue(withIdentifier: "QBResultVC", sender: QBName)
As in Tiago's answer, you can create a new struct or class which has QBName and QBBust properties. In addition, you can also use tuple in Swift.
This is an example:
in Destination ViewController
declare var QBInfo:(name: String, bust: Double)?
and in the button function that triggers the segue
let QBInfo = (name: QBName, bust: QBBust)
performSegue(withIdentifier: "QBResultVC", sender: QBBust)
then in prepareForSegue:sender:method
destination.QBInfo = QBInfo
This question is duplicate, but you can create a Struct or new Class, and storage your data how properties and send the 'transport' object in segue.
For detail, look this answser:
Swift sending Multiple Objects to View Controller

UserDefault not being saved when going back to viewcontroller 1 and then back to viewcontroller 2

Okay so I have two view controllers. One view controller loads up all the cells that is on my Plist and the second view controller opens up the cell and shows you the description. For example:
View Controller 1:
Dog
Cat
Mouse
Click on Dog cell it will take you to View Controller 2:
Dog goes Woof.
view controller 1 is written:
ovverride func prepare(for segue: UIStoryBoardSegue, sender: Any?) {
if segue.identifier == "showDetail" {
let animals: Animals
if isFiltering() {
animals = filteredData[indexPath.row]
}
else {
animals = originalData[indexPath.row]
}
let controller = (segue.destination as! UINavigationController).topViewController as! SecondViewController
controller.detailedAnimals = animals
controller.navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem
contrller.navigationItem.leftItemsSupplementBackButton = true
}
}
}
this is what i wrote in viewcontroller 2 updated
var isFavorite : Bool = false
#IBAction func addToFav(_ sender:UIButton) {
isFavorite = !isFavorite
UpdateButtonAppearance()
saveData()
}
private func UpdateButtonAppearance(){
if isFavorite{
let image = UIImage(named: "addFav")
favButton.setImage(image, for: . normal)
savedData()
}
else {
let image = UIImage(named: "addFavFilled")
favButton.setImage(image, for: . normal)
savedData()
}
}
ovveride func viewDidLoad(){
UpdateButtonAppearance()
saveData()
}
//updated code
func getFilePath () -> String {
var path: [AnyObject] = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true) as [AnyObject]
let documentsDirectory: String = path[0] as! String
let filepath = documentsDirectory.appending("Animals.plist")
return filepath
}
func saveData(){
let myDict : NSMutableDictionary = NSMutableDictionary()
myDict["fav"] = NSNumber(booleanLiteral: isFavorite)
myDict.write(toFile: self.getFilePath(), atomically: true)
}
func getBool(){
if FileManager.default.fileExists(atPath: self.getFilePath()) {
var myDict = NSDictionary(contentsOfFile: self.getFilePath()) as!
[String:AnyObject]
let myBool: Bool = myDict["fav"]!.boolValue
isFavorite = myBool
}
I saw a tutorial on how to change the bool in the Plist and wrote it this way. The code compiles but I don't think it is changing the bool value. So on my Animals Plist i have a Item 0 type dictionary, first key is called Animal, type is string, value is "dog" and second key is Description, type is string, value is "dog goes woof" and third key is called fav, type is Bool and value is No for now but I am trying to change this value to Yes but it is not working. Also thank you so much for your comment it was easy to follow and easy to understand.
The star is not filled when you go back to the 2nd view controller because you set the images in the #IBAction func addToFav(_ sender:UIButton) method. That is when you tap the button.
You should have that part of the code in another method that you also call in didLoad().
2nd View Controller should be something like this:
var isFavorite = UserDefaults.standard.bool(forKey: "isFavorite")
func didLoad() {
super.didLoad()
updateButtonAppearance()
}
private updateButtonAppearance() {
if isFavorite {
let image = UIImage(named: "addFavFilled")
button.setImage(image, for: . normal)
}
else {
let image = UIImage(named: "addFav")
button.setImage(image, for: . normal)
}
}
#IBAction func addToFav(_ sender:UIButton) {
isFavorite = !isFavorite
UserDefaults.standard.set(isFavorite, forKey: "isFavorite")
updateButtonAppearance()
}
Also the code could be improved to not save the variable in UserDefaults in addToFav method, but whenever isFavourite is changed. Maybe you will later want to change the state of isFavourite in another method which will require code duplication.
Also note that you save a value for all pets in UserDefaults under isFavourite. That means if you favor one pet, all other pets will be favored and vice versa. Consider replacing the bool value in user defaults with a dictionary that has keys for each pet and booleans as values.

How to change an array that is inserted on a TableCell from information I have in another ViewController?

I need your help! I don´t know how to change an array that is inserted on a TableCell from information I have in another ViewController. It’s a little bit messed up, but I’m gonna show you by my code.
Here I have a ViewController conformed by many switches that correspond to different categories of coupons, this is the code:
class FiltersViewController: UIViewController {
#IBOutlet weak var restaurantsSwitch: UISwitch!
#IBOutlet weak var sportsSwitch: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func returnHome(_ sender: Any) {
let vc = self.storyboard!.instantiateViewController(withIdentifier: "home") as! HomeViewController
self.present(vc, animated: false, completion: nil)
}
#IBAction func restaurants(_ sender: UISwitch) {
if restaurantsSwitch.isOn == true{
tuxtlaSwitch.isOn = false
sevillaSwitch.isOn = false
coapaSwitch.isOn = false
coyoacanSwitch.isOn = false
universidadSwitch.isOn = false
polancoSwitch.isOn = false
}
}
#IBAction func sports(_ sender: UISwitch) {
if sportsSwitch.isOn == true{
tuxtlaSwitch.isOn = false
sevillaSwitch.isOn = false
coapaSwitch.isOn = false
coyoacanSwitch.isOn = false
universidadSwitch.isOn = false
polancoSwitch.isOn = false
}
}
}
I’ve only show you two switches at the example with the purpose of not filling this with many code, but there are like 15 switches.
And in the other ViewController, which is connected to this one, the HomeViewController, contains coupons that comes from a JSON, and conforms an array of ten items displayed on a TableViewCell, the code:
class HomeViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var data : NSArray = []
var mainData : NSArray = []
var couponsImg : [UIImage] = []
var couponsTitle : [String] = []
var couponsDesc : [String] = []
var couponsCat : [String] = []
func getCoupons(){
let miURL = URL(string: RequestConstants.requestUrlBase)
let request = NSMutableURLRequest(url: miURL!)
request.httpMethod = "GET"
if let data = try? Data(contentsOf: miURL! as URL) {
do {
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? NSDictionary
let parseJSON = json
let object = parseJSON?["object"] as! NSDictionary
let mainCoupon = object["mainCoupon"] as! NSArray
let coupons = object["coupons"] as! NSArray
self.mainData = mainCoupon
self.data = coupons
self.couponImg1 = (mainCoupon[0] as AnyObject).value(forKey: "urlImage") as! String
self.couponImg2 = (mainCoupon[1] as AnyObject).value(forKey: "urlImage") as! String
self.couponTitle1 = (mainCoupon[0] as AnyObject).value(forKey: "nameStore") as! String
self.couponTitle2 = (mainCoupon[1] as AnyObject).value(forKey: "nameStore") as! String
self.couponDesc1 = (mainCoupon[0] as AnyObject).value(forKey: "promoDescription") as! String
self.couponDesc2 = (mainCoupon[1] as AnyObject).value(forKey: "promoDescription") as! String
self.couponCat1 = (mainCoupon[0] as AnyObject).value(forKey: "category") as! String
self.couponCat2 = (mainCoupon[1] as AnyObject).value(forKey: "category") as! String
self.couponsImg = [couponImage1!, couponImage2!, couponImage3!, couponImage4!, couponImage5!, couponImage6!, couponImage7!, couponImage8!, couponImage9!, couponImage10!]
self.couponsTitle = [couponTitle1, couponTitle2, couponTitle3, couponTitle4, couponTitle5, couponTitle6, couponTitle7, couponTitle8, couponTitle9, couponTitle10]
self.couponsDesc = [couponDesc1, couponDesc2, couponDesc3, couponDesc4, couponDesc5, couponDesc6, couponDesc7, couponDesc8, couponDesc9, couponDesc10]
self.couponsCat = [couponCat1, couponCat2, couponCat3, couponCat4, couponCat5, couponCat6, couponCat7, couponCat8, couponCat9, couponCat10]
} catch {
let error = ErrorModel()
error.phrase = "PARSER_ERROR"
error.code = -1
error.desc = "Parser error in get Notifications action"
}
}
}
#IBAction func showFilters(_ sender: Any) {
let vc = self.storyboard!.instantiateViewController(withIdentifier: "filters") as! FiltersViewController
self.present(vc, animated: false, completion: nil)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! HomeTableViewCell
cell.couponImg.image = couponsImg[indexPath.row]
cell.couponTitle.text = couponsTitle[indexPath.row]
cell.couponDescription.text = couponsDesc[indexPath.row]
cell.couponCategory.text = couponsCat[indexPath.row]
return cell
}
(Again I’ve only showed you two coupons for the example). The thing is that I need to apply some filters to the coupons on the TableCell. The first time the view appear it shows the 10 coupons correctly, but when I go to the filters an put it some of them ON it doesn’t make a difference, the method I was trying to use was something like this, first have an instance of the FiltersViewController class:
var filters = FilterViewController()
if filters.isMovingToParentViewController == true {
if filters.restaurantsSwitch.isOn == false {
self.couponsImg.remove(at: 0)
self.couponsImg.remove(at: 1)
self.couponsImg.remove(at: 2)
}
if filters.sportsSwitch.isOn == false {
self.couponsImg.remove(at: 3)
self.couponsImg.remove(at: 4)
self.couponsImg.remove(at: 5)
}
}
In the example bellow I’m trying to say that if a have the restaurant switch off, I’m going to delete the corresponding coupons of the restaurant category, and the same with the sports switch. But first of all I don’t know where to include this logic, in which method? And also I don’t know if this instruction is correct for my purposes. Can somebody give me a hand please???
Your logic is not working because you're instantiating a new FilterViewController, different from the FilterViewController associated with you screen.
You can solve this using delegate.
First, create the delegate:
protocol FilterDelegate {
func updateTable() }
Then, In your FilterViewController add this line:
weak var delegate:FilterDelegate?
You HomeViewController have to conform with this delegate, so:
class HomeViewController: FilterDelegate ... {
func updateTable() {
/* GET THE DATA FILTERED HERE */
tableview.reloadData()
}
In your FilterViewController:
#IBAction func returnHome(_ sender: Any) {
let vc = self.storyboard!.instantiateViewController(withIdentifier: "home") as! HomeViewController
self.delegate = vc
self.present(vc, animated: false, completion: nil)
delegate?.updateTable()
}
I think that should work.
EDIT:
Another approach is to create a segue between these two vcs and pass the which filters are active using the "prepare" function . Then you can take this information in your HomeVC and load your table based on the filters in the viewDidLoad function.
1 - Create a object Filters:
class Filters {
var tuxtlaSwitchIsOn: Bool
var sevillaSwitchIsOn: Bool
...
init(tuxtlaSwitchIsOn: Bool, sevillaSwitchIsOn: Bool, ...) {
self.tuxtlaSwitchIsOn = tuxtlaSwitchIsOn
self.sevillaSwitchIsOn = sevillaSwitchIsOn
...
}
}
2 - Add a attribute Filters to your HomeVC
class HomeViewController : ... {
...
var filtersActive: Filters?
...
}
3 - In your FilterViewController instantiate a Filter object indicating which filters are on
4 - In your FilterViewController prepare funs pass the Filter object to HomeVC
5 - In your HomeVC, get the Filter object and filter your data based on it.
Sure here is what you need. So you have a set of array filled with data and you want to apply filter on them. First, you need to create another array for filter results. This is because when user removes the filter, you still want to show the full list. To simplify, say you only have an array Foo: [String]. So you need to create another array called FooFiltered: [String] to hold the search result. Your can leave it empty when the view controller is loaded.
Next, in your filter section, it's recommended to use array filter technology like this post, but it's okay if you want to do it in your way. So all you need to do is to get elements from Foo array that match certain criteria and copy them into FooFiltered array. Here let me show you an example of doing filter manually
func filter() {
FooFiltered = [String]() //Clean up every time before search
for str in Foo {
if str == "criteria" {
FooFiltered.append(str)
}
}
}
Now you have a list of filtered items. You need a flag to tell table view which set of array to display. Say you have a flag called showSearchResult that is set to false originally. When you do the filter, set it to true. So your cellForRow will look like
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if showSearchResult {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! Cell
cell.textField.text = FooFiltered[indexPath.row]
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! Cell
cell.textField.text = Foo[indexPath.row]
return cell
}
}
You also need to update this flag to all your table view delegate method, like numberOfRowsInSection, etc.
Finally, with these codes, your table view is configured to show full results or filtered results base on the flag and you are setting that flag in the filter() function. The last thing to do is to ask tableView to reload data when the filter is done. So modify your filter function like this and you should be all set.
func filter() {
FooFiltered = [String]() //Clean up every time before search
showSearchResul = true
for str in Foo {
if str == "criteria" {
FooFiltered.append(str)
}
}
self.tableView.reloadData()
}

Resources