Retain The State Of On Off Button In A UITableViewCell - Swift - swift

I have been searching through many posts on SO but couldn't find an answer to this one.
I have a Table view listing various items. Each cell in the table view has a button that swaps an image around when clicked on, effectively working as an "on" or "off" button to show a user which items in the list they have selected. I have a variable inside my custom Cell Prototype class which stores a value of true or false which is updated every time the button is clicked on.
There is a "Done" button in the Table View that when tapped on calls an unwind Segue to go back to the first View Controller.
When the user taps on the Enter Table View button (on the first View Controller) to display the Table the buttons all go back to their default state, am guessing because each time the segue to the Table View Screen happens it creates a new instance of the Table to be displayed.
What I'm trying to achieve is that the state of the button (either on or off) is retained when going back into the Table screen. I've tried for a while sending an Integer value back from the TableCell Class (using a delegate) to the first View controller and then passing that value back into the Table View controller when the forward segue is called in order to have a "retained from the previous state value" that can be compared against when the cells are created to indicate if a button had been clicked or not. Couldn't get it to work though to save the state of the buttons. The list of Items in the Table will also change depending on what a user adds.
Some Screen shots and the code are below. The code hasn't got the delegate i was trying included (as it didn't work) but if its needed I will edit the post to include it. Any help would be hugely appreciated. Many Thanks!!
Code I have so far is:
//Main View Controller//
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
//segue to the Table View Screen
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "EnterTableView" {
let newTableView = segue.destination as! TableViewController
}
}
// Unwind Segue Called on Exit From Table View
#IBAction func unwindToMainViewController (_segue:UIStoryboardSegue) {
}
}
// Table View Controller //
class TableViewController: UITableViewController {
var dataArray = ["A", "B", "C", "D"]
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
//create one section for table
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//create number of rows based on the number of items in the dataArray set above
return dataArray.count
}
// an array that will contain all the cells
var cellArray = [UITableViewCell] ()
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//create a new cell based on the cell class "TableViewCell"
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath) as? TableViewCell
// variable to contain a single element from the dataArray - updates every time a new cell is created
let dataArrayForCells = dataArray [indexPath.row]
//set cell label text to show the value from the dataArrayForCells
cell!.label.text = dataArrayForCells
cellArray.append(cell!)
return cell!
}
// Table Cell Class//
class TableViewCell: UITableViewCell {
#IBOutlet weak var label: UILabel! // label to hold display some text
#IBOutlet weak var buttonImage: UIImageView! //image for button
var buttonClicked = true //variable to contain weather the has been clicked
//if button is tapped on run the below
#IBAction func aButton(_ sender: Any) {
//if button is clicked is true, swap image to red ("on") button, set buttonClicked value to false
if buttonClicked {
buttonImage.image = #imageLiteral(resourceName: "Rec Button Red")
buttonClicked = false
}
// if buttonClicked value is false swap image to grey ("off") button set buttonClicked value back to true
else {
buttonImage.image = #imageLiteral(resourceName: "Rec Button Grey")
buttonClicked = true
}
}

Related

Any idea as to why my data isn't being passed to the new tableview?

I am passing data from one tableview to another. I want the category data that the tableviewA contains to be passed to tableviewB. When I perform the segue, the print data that I have for TableviewB is empty.
This is tableviewA
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let category = listOfCategories[indexPath.row].strCategory
let vc = MealsByCategoryVC()
vc.mealCategory = category
print(category) // Properly returns the category
performSegue(withIdentifier: "searchMeals", sender: nil)
}
This is tableview2
class MealsByCategoryVC: UITableViewController {
var mealCategory : String = ""
var listOfMeals : [Meals] = []
override func viewDidLoad() {
super.viewDidLoad()
print("Meal category is \(mealCategory)") //This statement returns "Meal category is "
}
This:
let vc = MealsByCategoryVC()
vc.mealCategory = category
performSegue(withIdentifier: "searchMeals", sender: nil)
...is not how you pass a value into a view controller that you are creating by calling performSegue. The first two lines of that code do nothing at all! The view controller created by the segue is different from the one you are creating by saying MealsByCategoryVC(); in fact, the latter is just thrown away, uselessly. You are setting the mealCategory of the wrong view controller instance.
Instead, implement prepare(for:sender:). That's what it's for. You receive the segue and its destination view controller. That is the view controller whose mealCategory you need to set.

Trouble with segue/unsegue when making a list app

I'm new to coding, so please bear with me. I was following an online tutorial that worked with plists to make a habit list app. I have a table view controller that shows a list of habits and a segue that presents modally a view controller that has text fields to add a habit.
enter image description here
Every time it runs, nothing happens when I click on the "save" and "cancel" buttons. I realize this is a vague question as it doesn't pinpoint to a specific issue, but I am really struggling with fixing this issue and would really appreciate if someone proofreads the code. The app builds and runs with no warnings.
This is the table view controller that shows the habits:
class TableViewController: UITableViewController {
//MARK: Properties
var habits = [Habit]()
//MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return habits.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Table view cells are reused and should be dequeued using a cell identifier.
guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell") else {
fatalError("The dequeued cell is not an instance of ViewController.")
}
// Fetches the appropriate habit for the data source layout.
let habit = habits[indexPath.row]
cell.textLabel?.text = habit.mainGoal
cell.detailTextLabel?.text = habit.microGoal
return cell
}
#IBAction func unwindToHabitList(sender: UIStoryboardSegue) {
if let source = sender.source as?ViewController, let habit = source.habit {
//add a new habit
let newIndexPath = IndexPath(row: habits.count, section: 0)
habits.append(habit)
tableView.insertRows(at: [newIndexPath], with: .automatic)
}
}
This is the view controller that adds a habit:
class ViewController: UIViewController, UITextFieldDelegate, UINavigationControllerDelegate {
#IBOutlet weak var saveButton: UIBarButtonItem!
#IBOutlet weak var mainGoalTextField: UITextField!
#IBOutlet weak var microGoalTextField: UITextField!
var habit: Habit?
//method for configuring controller before presenting
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
//configure this destination view controller only when save button is pressed
guard let button = sender as? UIBarButtonItem, button === saveButton else {
os_log("save button was not pressed, cancelling", log: OSLog.default, type: .debug)
return
}
let mainGoal = mainGoalTextField.text ?? ""
let microGoal = microGoalTextField.text ?? ""
//set the habit to be passed on to tableViewController after the unwind segue
habit = Habit(mainGoal: mainGoal, microGoal: microGoal)
}
#IBAction func cancel(_ sender: UIBarButtonItem) {
// Depending on style of presentation (modal or push presentation), this view controller needs to be dismissed in two different ways.
let isPresentingInAddHabitMode = presentingViewController is UINavigationController
if isPresentingInAddHabitMode {
dismiss(animated: true, completion: nil)
}
else if let owningNavigationController = navigationController{
owningNavigationController.popViewController(animated: true)
}
else {
fatalError("The ViewController is not inside a navigation controller.")
}
}
I appreciate any and all help in advance!
STORYBOARD CONNECTIONS:
TABLEVIEW CONTROLLER CONNECTIONS
ADD HABIT VIEW CONTROLLER CONNECTIONS

Xcode 8.3.3 Swift 3 - Variable not updating between files

I'm having a issue with xcode, since I updated to 8.3.3. I usually work with the MCV (Model - View - Controller) method, and now, my variables aren't updating between them.
Situation: I Have a Model (store all major variables and calculations functions); a TableView Controller (Control Tableview) and TableViewCell (set Outlet and actions)
Goal: When a button is pressed in a cell, it should add a row in TableView.
Problem: Why isn't table view getting the new value of Model() variable.
To make it better to understand, here is a timeline of what is going on :
Run > run viewDidLoad in TableViewController > update variable test in Model() > cellForRowAt is called and prints ["1"] > show tableView with 1 row > press button > print ["1"] > add ["2"] to Model() > print ["1","2"] > post notification > viewDidLoad gets Notification and prints "reloading table" > cellForRowAt is called and prints ["1"] > tableView keeps 1 row.
Here is one example of my code:
I have my Model.Swift:
class Model {
var test : [String] = []
}
My TableViewController:
class BudgetTableViewController: UITableViewController {
let model = Model()
override func viewDidLoad() {
super.viewDidLoad()
model.test.append("1")
center.addObserver(forName: NSNotification.Name(rawValue: "reloadTableVIew"), object: appDelegate, queue: queue) {[unowned self] (_) in
print("reloading table")
self.tableView.reloadData()
}
}
To simplify.. in Sections I keep returning "1" and for rows I count the variable test in Model().
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print(model.test) // **ALWAYS PRINT ["1"]**
let cell = tableView.dequeueReusableCell(withIdentifier: "buttonCell", for: indexPath) as! TableViewCell
return Cell
}
And my TableViewCell:
class TableViewCell: UITableViewCell{
let model = BudgetModel()
let notification = Notification(name: Notification.Name(rawValue: "reloadTableVIew"), object: appDelegate)
#IBAction func okButton(_ sender: UIButton) {
print(model.test)
model.test.append("2")
print(model.test)
NotificationCenter.default.post(notification)
}
}
I hope it is clear enough.
The point is, if I press the button 5 times, it will add the string 5 times to the array (that is confirmed in the print) but when cellForRowAt is called, it will print ["1"] always.
THank you for your help
The two model vars are in different classes. You have var model in your viewController, & var model in your cell. Changing one isn't going to affect the other. You'd be best to implement a delegate for your cells, where the viewController is the delegate, and the cell calls it when pressed -
protocol MyCellDelegate: class {
func cellWasPressed()
}
In the cell -
weak var delegate: MyCellDelegate?
Set this to the viewController when you create the cell.
Then in the button pressed method, add -
self.delegate?.cellWasPressed()
In the viewController, implement this -
func cellWasPressed() {
self.model.test.append("2")
self.tableView.reloadData()
}
I don't think you need to be using a notification, this is far simpler.

How would I unhide a button if user performs segue in swift?

I have a button inside a cell (PFQueryTableViewController) that is hidden and I want to unhide it when the user performs a certain segue that I call programatically.
When the user taps the cell it segues to a view controller which displays the contents of the cell full screen... I want the button to unhide in this cell when the segue is called so when the user goes back to the table of cells they can see it on the cell they just tapped.
How can I do this?
Edit after questions:
inside cellRowForIndexPath I have the following for the button
cell.myButton.tag = indexPath.row
cell.myButton.addTarget(self, action: "pressed:", forControlEvents: UIControlEvents.TouchUpInside)
cell.myButton.hidden = true
And the segue itself carries information from the cell (stored in Parse backend) across to FullPostViewController from AllPostsTableViewController. The code for that is this (would I call the unhide in here somewhere?):
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("showFullPost", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showFullPost" {
let indexPath = self.tableView.indexPathForSelectedRow
let fullPostVC = segue.destinationViewController as! FullPostViewController
let object = self.objectAtIndexPath(indexPath)
fullPostVC.post = object?.objectForKey("postContent") as? String
let likeCount = object!.objectForKey("likedBy")!.count
fullPostVC.likesCounted = String(likeCount)
self.tableView.deselectRowAtIndexPath(indexPath!, animated: true)
}
}
(Answer thoroughly edited after thorough edit of question)
One possible solution follows below.
Since you mention table cells (each containing a button; I'll assume UIButton), I assume you populate your table view cells with UITableViewCell objects; some fancy subclass to the latter. In this class:
If you haven't already, create an #IBOutlet from your button as a property in this class.
Overload the method setSelected(...) to un-hide your button in case the UITableViewCell is selected (which will precede the segue)
Hence, in your UITableViewCell subclass, you should be able to do something along the lines:
// ...TableViewCell.swift
Import UIKit
// ...
class ...TableViewCell: UITableViewCell {
// Properties
#IBOutlet weak var button: UIButton!
// button contained in UITableViewCell
// ...
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// If table cell is selected (after which segue will follow),
// un-hide button.
if (selected) {
button.hidden = false
}
}
}
Hopefully this will achieve your goal.

Fill a textField with a selected TableViewCell?

In my initial view there is a blank textField (with an invisible button over it) that segues to a TableView when you click it. I want to send the text data from my TableViewCell selection to the blank textField in the original view.
I've made a View2.swift file, which is a replica of my original View. I was trying to edit the source code in there to push the textData by sending the .text from the indexPath of the TableView.
This video shows how to send data from a first view to a second view, but I'm trying to send data from my second view (TableView) back to my first view (View).
Here is my code:
ViewController.swift
import UIKit
class ViewController: UITableViewController {
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var DestViewController : View2 = segue.destinationViewController as! View2
DestViewController.formulaSelectionText = ViewController.
}
}
View2.swift:
import Foundation
import UIKit
class View2: UIViewController {
override func viewDidLoad(){
formulaSelection.text = indexPathForCell(Cell: UITableView)
}
}
I assume it would be best to just update the value in the initial view. I'm thinking with a UITextFieldDelegate?
It is easy to pass back UITableViewCell Selected value back to a controller. Simply on selection of a cell, update the textfield's value in previous controller and pop the controller/dismiss it if you are using navigation controller / modal controller.
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
//get last controller in hierarchy
let viewControllers: [UIViewController] = self.navigationController!.viewControllers as [UIViewController];
var prevController : PrevViewController = viewControllers[viewControllers.count - 1] as PrevViewController;
prevController.textfield.text = dataSource[indexPath.row]; //update textfield here
self.navigationController!.popViewControllerAnimated(true);
}
Hope it helps!