Inserting rows to tableView using modal popup - ios

I'm making a basic fitness app and within it I have a food diary that tracks the food you eat on a daily basis. I have a modal popup (picture below) that shows when you tap "add food". I'm having trouble inserting rows from the modal popup when the button "Save to Food Diary" is pressed. I'm also trying to update the calories label to the current calories the user accumulated throughout the day. Heres the code i have so far:
class PopupVC: UIViewController {
var section: Int?
var caloriesLabel = " "
var tableData: [String] = [""]
let foodDiary = FoodDiary()
var caloriesCell = caloriesForDiary()
#IBOutlet weak var foodTimeLabel: UILabel!
#IBOutlet weak var foodPopup2: UIView!
#IBOutlet weak var foodPopUp: UIView!
#IBOutlet weak var inputFood: UITextField!
#IBOutlet weak var inputCals: UITextField!
#IBAction func saveToDiary(_ sender: Any) {
if (inputFood.text?.isEmpty)! || (inputCals.text?.isEmpty)! {
return
}
caloriesLabel = foodDiary.testVariable
tableData.append(inputFood.text!)
foodDiary.tableView.beginUpdates()
foodDiary.tableView.insertRows(at: [IndexPath.init(row: (tableData.count) - 1, section: section!)], with: .automatic)
foodDiary.tableView.endUpdates()
dismiss(animated: true, completion: nil)
}
Basically i'm trying to update my FoodDiaryVC using my PopUpVC, and i'm struggling to pass the data between both controllers to insertRows and to update my calories label. Hope this was explained well enough!

The iOS approach would be to create a delegate.
#protocol AddRowDelegate {
func didAddRow(name : String, calories : String)
}
Then your FoodDiaryVC should implement it :
#class FoodDiaryVC : UIViewController, AddRowDelegate {
///Your code
}
Add a delegate variable to your PopUp Class
class PopUpVC : UIVIewController {
weak var delegate : AddRowDelegate?
}
No you have to set the delegate when you show the Popup
myPopUp.delegate = self
When the user tap the "Add to diary", just call the delegate
delegate?.didAddRow(name: "blah", calories : "blah")

Related

What should I change in my code to make textfield in cell not to be nil after dismiss VC

Community!
I have some issue.
code:
protocol CellDelegate: class { }
class Cell: UITableViewCell {
.....
#IBOutlet weak var valueTextField: UITextField!
weak var delegate: CellDelegate?
.....
}
didSelectRowAt indexPath presents my ContainerView by overFullScreen, where I have:
class ContainerViewController: UIViewController {
.....
#IBOutlet private weak var fromTextField: UITextField!
#IBOutlet private weak var toTextField: UITextField!
#IBAction private func performChange(_ sender: UIButton) {
if let from = Int(fromTextField.text!), let to = Int(toTextField.text!) {
let cell = Cell()
cell.delegate = self
cell.valueTextField = "\(from) - \(to)"
self.dismiss(animated: false, completion: nil)
}
}
.....
}
extension ContainerViewController: CellDelegate { }
printDebug tells me that performChange sending my data well to other class, but cell.valueTextField is being nil when ContainerViewController dismiss and app crash
I know that I can make valueTextField.text = "1 - 5" //example from 1 to 5
with help of alert, but I need to make it with other viewController.
Please help me
You shouldn't use
let cell = Cell()
but instead add a delegate to the previous vc when you present the containervc (inside didSelectRowAt) and send the data before the dismiss , then change the array model of the table and finally refresh the table

Disable all UIButtons in UITableViewCell once a separate UIButton is pressed

Problem I've found some questions asking how to disable a particular cell button in a table view, but what I want to do is to disable all instances of a button within a table view cell when another button is pressed.
Details I have a table view which is displaying a list of exercises and number of reps in a custom cell. Within the custom cell is also a button "swap" which allows a user to swap an exercise for another one before the workout starts.
When the user hits "start workout" (which triggers a timer) I want to disable all instances of the swap button (grey them all out and make non clickable).
Code
My workout cell class is here :
class WorkoutCell : UITableViewCell {
var delegate: WorkoutCellDelegate?
#IBAction func swapButtonPressed(_ sender: Any) {
delegate?.swapButtonTapped(cell: self)
}
#IBOutlet weak var exerciseName: UILabel!
#IBOutlet weak var repsNumber: UILabel!
}
protocol WorkoutCellDelegate {
func swapButtonTapped(cell: WorkoutCell)
}
What have I tried
The way I thought to do this was to add an IBOutlet (e.g. 'swapButton') for the button and then simply do something like :
#IBAction func startButtonPressed(_ sender: UIButton) {
WorkoutCell.swapButton.isenabled = false
}
But Xcode doesn't allow you to add IBOutlets to repeating cells so I'm a bit stuck.
I'm fairly new to delegates (managed to get it working for displaying the table view) so if it has something simple to do with that sorry!
Add a property to your viewcontroller:
var swapButtonsDisabled : Bool = false
In your cellForRow do something like this:
cell.swapButton.isEnabled = !self.swapButtonsDisabled
When the start button is pressed, set swapButtonDisabled to true and reload the tableView.
1- As you connect
#IBOutlet weak var exerciseName: UILabel!
create outlet for every btn
#IBOutlet weak var btn1: UIButton!
2- Add a property to the model array in the VC to hold the state of every cell
3- When you click the main btn fire the delegate method with the btn's cell
4- In VC delegate handle method disable the btns and change the state of the array index path
5- Don't forget to check state in cellForRow
You are pretty close. First I suggest you to be more specific and have the data you need in cell and use access control:
class WorkoutCell : UITableViewCell {
var workoutSwappable: (workout: Workout, canBeSwapped: Bool)? {
didSet {
swapButton.isEnabled = workoutSwappable?.canBeSwapped == true
// TODO: do additional setup here
}
}
weak var delegate: WorkoutCellDelegate? // Needs to be weak or you will have memory leaks
#IBAction private func swapButtonPressed(_ sender: Any) {
if let workoutSwappable = workoutSwappable, workoutSwappable.canBeSwapped == true {
delegate?.workoutCell(self, didTapWorkoutSwap: workoutSwappable.workout)
}
}
#IBOutlet private var exerciseName: UILabel!
#IBOutlet private var repsNumber: UILabel!
#IBOutlet private var swapButton: UIButton!
}
Ok so now in cell for row at index path all you need is something like:
cell.workoutSwappable = (self.items[0], self.canSwap)
On delegate you now have:
func workoutCell(_ sender: WorkoutCell, didTapWorkoutSwap workout: workout) {
self.currentWorkout = workout
self.canSwap = false
self.initializeTimer()
tableView.reloadData() // This will now flush all the buttons
}

Swift delegate function not being called from protocol

I am making a library of sorts. One screen, a table view controller, contains a list of books and the other screen, a view controller, allows you to add books.
I'm using the Add Book class to add books to the list of books screen. The add books screen contains text inputs to allow user to enter the book's name, author, publisher etc. When a user clicks a button after entering those fields, it creates a book object containing those attributes, and the user is directed back to the home screen showing the new book added to the list of books. The list of books is a table of rows. Each row contains one book respectively.
I'm using a protocol to call the delegate function in the list of books class, however it's not calling this function. The function not being called is newBook. My code seems fine to me, yet the newBook delegate function is not being called. Please help!. I've added relevant code below:
Add Book class:
import UIKit
protocol AddBookProtocol {
func newBook(book: Book)
}
class AddBookViewController: UIViewController {
var addBookDelegate: AddBookProtocol?
#IBOutlet weak var authorField: UITextField!
#IBOutlet weak var genreField: UITextField!
#IBOutlet weak var editionField: UITextField!
#IBOutlet weak var isbnField: UITextField!
#IBOutlet weak var nameField: UITextField!
#IBOutlet weak var dateField: UITextField!
#IBOutlet weak var publisherField: UITextField!
#IBOutlet weak var descriptionField: UITextField!
#IBAction func saveButton() {
print("HAHHHAHAHAH")
let name = nameField.text
let isbn = isbnField.text
let author = authorField.text
let publisher = publisherField.text
let publishDate = dateField.text
let genre = genreField.text
let edition = editionField.text
let desc = descriptionField.text
let book = Book(title: name!, isbn: isbn!, author: author!, publishDate: publishDate!, genre: genre!, publisher: publisher!, edition: edition!, desc: desc!)
addBookDelegate?.newBook(book: book)
navigationController?.popViewController(animated: true)
}
//rest of the code
Class displaying list of current books:
class CurrentBooksTableViewController: UITableViewController, AddBookProtocol {
var BooksList: [Book] = []
func newBook(book: Book) {
BooksList.append(book)
tableView.beginUpdates()
tableView.insertRows(at: [IndexPath(row: BooksList.count - 1, section: 0)], with: .automatic)
tableView.endUpdates()
tableView.reloadSections([SECTION_COUNT], with: .automatic)
}
//rest of the code
}
newBook is not being called for some reason.
Have you set the value of addBookDelegate? You can do this in the prepare for segue function in your table view controller.
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let addBookVC = segue.destination as? AddBookViewController {
addBookVC.addBookDelegate = self
}
}
You need to set the addBookDelegate for AddBookViewController as CurrentBooksTableViewController
Inside CurrentBooksTableViewController before presenting AddBookViewController you need to set addBookDelegate to self.
INSTANCE_OF_ AddBookViewController.addBookDelegate = self
Then only you can access the addBookDelegate functions from other side. otherwise if you check addBookDelegate will be = nil

Swift 3 pass values to Controller on UICollectionViewCell button click

I am beginner in iOS development and I have implemented following screen using UICollectionView :
CollectionViewCell File Code is:
import UIKit
class EventCell: UICollectionViewCell{
var classEvent: Event?
#IBOutlet weak var eventTitle: UILabel!
#IBOutlet weak var eventTeams: UILabel!
#IBOutlet weak var eventTime: UILabel!
#IBOutlet weak var eventTeamOneImage: UIImageView!
#IBOutlet weak var eventTeamTwoImage: UIImageView!
#IBOutlet weak var leaderboardButton: UIButton!
var datasourceItem: Any?{
didSet{
guard let event = datasourceItem as? Event else { return }
classEvent = event
eventTitle.text = "Match \(event.matchNo) (\(event.matchStage))"
eventTeams.text = "\(event.teamOne.nameAttr) vs \(event.teamTwo.nameAttr)"
eventTime.text = "\(event.getEventLockTimeAsString())"
eventTeamOneImage.loadImageUsingCache(withUrl: event.teamOne.flagPhoto)
eventTeamTwoImage.loadImageUsingCache(withUrl: event.teamTwo.flagPhoto)
leaderboardButton.addTarget(self, action: #selector(handleLeaderBoardClick), for: .touchUpInside)
}
}
#IBAction func leagueButton(_ sender: Any) {
}
weak var delegate: HomeControllerDelegate?
func handleLeaderBoardClick() {
if let matchId = classEvent?.id {
print(matchId)
delegate?.clickOnLeaderBoard(matchId: matchId)
}
}
}
Now on click on Leaderboard button(icon with 1,2,3) I would like to open new LeaderBoard Controller and pass matchId which is classEvent.id
How can I pass values to the new controller? And what is the best way to do that.
You can pass the match Id via segue:
In LeaderBoard Controller set a property:
var matchId:Int?
Set a segue between the controller and add an identifier:
On Click leaderboard button:
self.performSegueWithIdentifier("yourSegueIdentifier", sender: self)
Add the segue method:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue,identifier == "yourSegueIdentifier" {
let destinationVC = segue.destinationViewController as LeaderbardController
destinationVC.matchId = classEvent.id
}
}
}
Three easy steps to get what u want:
Make a BaseViewController class a subclass of UiViewController. This class would be the alternate of UiViewcontroller in your project,it means while creating any viewcontroller BaseViewController will be the parent class.
Declare a variable in BaseViewController.e.g- var data: Any?
Then while moving from a viewcontroller to another , simply assign any type of data to that variable declared in BaseViewController.
And in any lifecycle method of your new viewcontroller you will get that data using self.data.

Swift: set variable before performing segue

I've a tableView with a cell for each pokemon in the "pokedex". When I press a button in the Cell, I want to show a view with details about this creature.
What I do is that I have a global variable "currentPokemon", that I set to the requested pokemon when the button is pressed. Here's my code for the Cell :
class PokemonTableViewCell: UITableViewCell {
var pokemon: Pokemon!
#IBOutlet weak var pokemonImage: UIImageView!
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var releaseDateLabel: UILabel!
#IBAction func setPokemon(sender: UIButton) {
currentPokemon = self.pokemon
}
}
When I try to access the currentPokemon var in the details view, I get a fatal error because currentPokemon is nil. How could I get this code to be executed before the segue ?
You need to add store for selected Pokemon with singleton instance like:
struct Pokemon {
let name: String
}
class PokemonStore {
static let instance = PokemonStore()
var currectPokemon: Pokemon?
}
Later you can get saved pokemon in any place of your code with PokemonStore.instance.currectPokemon
If you are using segue, you should pass data to detail controller in this UIViewController method:
func performSegue(withIdentifier identifier: String, sender: Any?)

Resources