Set labels in a custom cell to names from an array using a separate controller - ios

I have a view controller with a table. The view controller has a button that segues to another controller with 4 textfields. There is a name textfield and three others where numbers can be inputed. When a user presses add, an array is appended and the view controller is dismissed while updating the table in the previous view controller. The user is then presented a table with a new cell added. The cell has 4 labels.
I am trying to figure out how to set each label in a cell to the 4 textfields that are in the view controller that created the new cell. Here is a picture of the view controller with a cell created:
Here is the view controller when the plus button is pressed that creates the cell by updating the array:
I would like to be able to set the far left label to represent the name that is added to the array every time. The first time a person clicks the plus button, the textfields view controller will pop up and the the user can put the name in the textfield. When the add button is pressed the label on the left would show the name in the controller that appended the array. The next name that is added through the view controller will be in the cell below the previous name entered and so forth. I need to get the array to show the text properly. I originally wanted the cells to have textfields that the user could put the info in there but the cells data wasn't sending to firebase database properly.
Here is the code for the first view controller:
import UIKit
import Firebase
class ConsiderationsTestViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var tableView: UITableView!
static var numberOfPeople: [String] = []
var AddPersonCell = "AddPersonCell"
#IBOutlet weak var CompanyImage: UIImageView!
#IBOutlet weak var companyNameTextFieldConsiderations: UITextField!
#IBOutlet weak var companyDescriptionTextFieldConsiderations: UITextField!
#IBOutlet weak var startDateTextFieldConsiderations: UITextField!
#IBOutlet weak var endDateTextFieldConsiderations: UITextField!
let datePickerS = UIDatePicker()
let datePickerE = UIDatePicker()
var database: Database!
var storage: Storage!
var selectedImage: UIImage?
var ref:DatabaseReference?
var databaseHandle:DatabaseHandle = 0
let dbref = Database.database().reference()
let uid = Auth.auth().currentUser?.uid
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
self.companyNameTextFieldConsiderations.delegate = self
self.companyDescriptionTextFieldConsiderations.delegate = self
// Set the Firebase reference
ref = Database.database().reference()
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(ConsiderationsTestViewController.handleSelectCompanyImageView))
CompanyImage.addGestureRecognizer(tapGesture)
CompanyImage.isUserInteractionEnabled = true
self.navigationController!.navigationBar.isTranslucent = false
navigationItem.backBarButtonItem = UIBarButtonItem(
title: "", style: .plain, target: nil, action: nil)
createDatePickerForStart()
createDatePickerForEnd()
NotificationCenter.default.addObserver(self, selector: #selector(loadList), name: NSNotification.Name(rawValue: "load"), object: nil)
}
#objc func loadList(){
//load data here
self.tableView.reloadData()
}
#objc func handleSelectCompanyImageView() {
let pickerController = UIImagePickerController()
pickerController.delegate = self
pickerController.allowsEditing = true
present(pickerController, animated: true, completion: nil)
}
#IBAction func AddPersonTapped(_ sender: Any) {
}
#IBAction func sendButtonTapped(_ sender: Any) {
let companyNameC = companyNameTextFieldConsiderations.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let companyDescriptionC = companyDescriptionTextFieldConsiderations.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let today = Date()
let formatter1 = DateFormatter()
formatter1.dateFormat = "MMM d y"
print(formatter1.string(from: today))
let todaysDate = formatter1.string(from: today)
let storageRef = Storage.storage().reference(forURL: "STORAGE URL HERE")
let imageName = companyNameTextFieldConsiderations.text!
let storageCompanyRef = storageRef.child("Company_Image_Considerations").child("\(todaysDate)").child(imageName)
let companyDescriptionTextFieldText = companyDescriptionTextFieldConsiderations.text
let dateToStart = startDateTextFieldConsiderations.text
let dateToDecide = endDateTextFieldConsiderations.text
let companyRef = Database.database().reference().child("Considerations").child("\(todaysDate)").child(imageName)
let considerationInfluencerRef = Database.database().reference().child("Considerations").child("\(todaysDate)").child(imageName).child("Users")
//let values = ["Feed_Quantity": "feedTFC", "Story_Quantity": "storyTFC", "Compensation": "compensationTFC"]
guard let imageSelected = self.CompanyImage.image else {
print ("Avatar is nil")
return
}
var dict: Dictionary<String, Any> = [
"Company Image": "",
"Company Description": companyDescriptionTextFieldText!,
"Start Date": dateToStart!,
"Decision Date": dateToDecide! ]
guard let imageData = imageSelected.jpegData(compressionQuality: 0.5) else {
return
}
let metadata = StorageMetadata()
metadata.contentType = "image/jpeg"
storageCompanyRef.putData(imageData, metadata: metadata, completion:
{ (StorageMetadata, error) in
if (error != nil) {
return
}
storageCompanyRef.downloadURL { (url, error) in
if let metadateImage = url?.absoluteString {
dict["Company Image"] = metadateImage
companyRef.updateChildValues(dict, withCompletionBlock: {
(error, ref) in
if error == nil {
print("Done")
return
}
}
)
}
}
storageRef.updateMetadata(metadata) { metadata, error in
if error != nil {
//Uh-oh, an error occurred!
} else {
// Updated metadata for 'images/forest.jpg' is returned
}
}
})
// considerationInfluencerRef.updateChildValues(values as [AnyHashable : Any]) { (error, ref) in
// if error != nil {
// print(error ?? "")
// return
// }
self.navigationController?.popViewController(animated: true)
// }
}
func createDatePickerForStart() {
// center text in field
startDateTextFieldConsiderations.textAlignment = .center
// toolbar
let toolbar = UIToolbar()
toolbar.sizeToFit()
// barbutton
let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: nil, action: #selector(donePressedStart))
toolbar.setItems([doneButton], animated: true)
// assign toolbar to textfield
startDateTextFieldConsiderations.inputAccessoryView = toolbar
// assign datePicker to text field
startDateTextFieldConsiderations.inputView = datePickerS
// date picker mode
datePickerS.datePickerMode = .date
}
func createDatePickerForEnd() {
// center text in field
endDateTextFieldConsiderations.textAlignment = .center
// toolbar
let toolbar = UIToolbar()
toolbar.sizeToFit()
// barbutton
let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: nil, action: #selector(donePressedEnd))
toolbar.setItems([doneButton], animated: true)
// assign toolbar to textfield
endDateTextFieldConsiderations.inputAccessoryView = toolbar
// assign datePicker to text field
endDateTextFieldConsiderations.inputView = datePickerE
// date picker mode
datePickerE.datePickerMode = .dateAndTime
}
#objc func donePressedStart() {
// formatter
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .none
startDateTextFieldConsiderations.text = formatter.string(from: datePickerS.date)
self.view.endEditing(true)
}
#objc func donePressedEnd() {
// formatter
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .medium
endDateTextFieldConsiderations.text = formatter.string(from: datePickerE.date)
self.view.endEditing(true)
}
#IBAction func testTapped(_ sender: Any) {
print(ConsiderationsTestViewController.numberOfPeople)
}
}
extension ConsiderationsTestViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
//print("did Finish Picking Media")
if let image = info[UIImagePickerController.InfoKey(rawValue: "UIImagePickerControllerEditedImage")] as? UIImage{
selectedImage = image
CompanyImage.image = image
}
dismiss(animated: true, completion: nil)
}
}
extension ConsiderationsTestViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return ConsiderationsTestViewController.numberOfPeople.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: AddPersonCell, for: indexPath) as! ConsiderationsCell
return cell
}
}
Here is the code for the second view controller:
import UIKit
import Firebase
class DropDownCellViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate, UITextFieldDelegate {
var numberOfPeople = [String]()
var users = [User]()
var userName = [String]()
var filteredNames: [String]!
let dropDownCell = "dropDownCell"
var emptyField = [String]()
override func viewDidLoad() {
super.viewDidLoad()
updateDataArray()
tableView.register(UserCell.self, forCellReuseIdentifier: dropDownCell)
filteredNames = userName
tableView.delegate = self
tableView.dataSource = self
nameTextField.delegate = self
tableView.isHidden = true
// Manage tableView visibility via TouchDown in textField
nameTextField.addTarget(self, action: #selector(textFieldActive), for: UIControl.Event.touchDown)
}
#IBOutlet weak var nameTextField: NoCopyPasteUITextField!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var gridTextField: UITextField!
#IBOutlet weak var storyTextField: UITextField!
#IBOutlet weak var compensationTextField: UITextField!
#IBAction func textFieldChanged(_ sender: AnyObject) {
tableView.isHidden = true
}
#IBAction func cancelButtonTapped(_ sender: Any) {
self.navigationController?.popViewController(animated: true)
}
#IBAction func addButtonTapped(_ sender: Any) {
ConsiderationsTestViewController.numberOfPeople.append("\(nameTextField.text!)")
self.navigationController?.popViewController(animated: true)
print(ConsiderationsTestViewController.numberOfPeople)
DispatchQueue.main.async {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "load"), object: nil)
}
}
let searchController = UISearchController(searchResultsController: nil)
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
{
guard let touch:UITouch = touches.first else
{
return;
}
if touch.view != tableView
{
nameTextField.endEditing(true)
tableView.isHidden = true
}
}
#objc func textFieldActive() {
tableView.isHidden = !tableView.isHidden
}
// MARK: UITextFieldDelegate
func textFieldDidEndEditing(_ textField: UITextField) {
// TODO: Your app can do something when textField finishes editing
print("The textField ended editing. Do something based on app requirements.")
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return filteredNames.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: dropDownCell, for: indexPath) as!
UserCell
let user = users[indexPath.row]
cell.textLabel?.text = user.name
if let profileImageUrl = user.profileImageUrl {
cell.profileImageView.loadImageUsingCacheWithUrlString(urlString: profileImageUrl)
}
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 72
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Row selected, so set textField to relevant value, hide tableView
// endEditing can trigger some other action according to requirements
nameTextField.text = userName[indexPath.row]
tableView.isHidden = true
nameTextField.endEditing(true)
}
//Mark: Search Bar Config
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
filteredNames = []
if searchText == "" {
filteredNames = userName
}
else {
for names in userName {
if names.lowercased().contains(searchText.lowercased()) {
filteredNames.append(names)
}
}
}
self.tableView.reloadData()
}
func updateDataArray() {
Database.database().reference().child("users").observe(.childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
let user = User()
//self.setValuesForKeys(dictionary)
user.name = dictionary["name"] as? String
user.email = dictionary["email"] as? String
user.facebookUrl = dictionary["facebookUrl"] as? String
user.instagramUrl = dictionary["instagramUrl"] as? String
user.linkedInUrl = dictionary["linkedInUrl"] as? String
user.profileImageUrl = dictionary["profileImageUrl"] as? String
user.twitterUrl = dictionary["twitterUrl"] as? String
user.id = dictionary["id"] as? String
user.userType = dictionary["userType"] as? String
self.users.append(user)
self.userName.append(user.name!)
self.filteredNames.append(user.name!)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}, withCancel: nil)
}
}
I am trying to append the label with a function that is called in the addButton function called renameLabel. Here is the func rename label code:
func renameLabel() {
let cell = ConsiderationsTestViewController.init().tableView.dequeueReusableCell(withIdentifier: AddPersonCell) as! ConsiderationsCell
cell.nameLabelC.text = ("\(ConsiderationsTestViewController.numberOfPeople)")
}
I am now getting the error:
Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
Any help would be awesome!

First of all create a protocol above second VC.
protocol UpdatingArrayDelegate: class {
func updateArray(newArray: [String])
}
And inside the second VC create:
var updatingArrayDelegate: UpdatingArrayDelegate!
Then before pushing to the second VC, set the delegate to self:
#objc func pushToSecondVCButtonTapped() {
let destVC = secondVC()
updatingArrayDelegate = self
self.navigationController?.pushViewController(destVC, animated: true)
// if you don't have self.navigationController use:
// present(destVC(), animated: true, completion: nil)
}
Now when finishing the edit on secondVC when pressing your addButton do the following:
#objc func doneButtonTapped() {
updatingArrayDelegate?.updateArray(newArray: myNewArrayCreatedOnThisSecondVC)
self.navigationController?.popViewController(animated: true)
}
Then add the delegate function on first
extension FirstVC: UpdatingArrayDelegate {
func updateArray(newArray: [String]) {
print("updateArray")
let myCell = myTableView.cellForRow(at: IndexPath(row: x, section: y)) as! MyCell
myCell.textLabel1.text = newArray[0]
myCell.textLabel2.text = newArray[1]
myCell.textLabel3.text = newArray[2]
myCell.textLabel4.text = newArray[3]
}
}
You can think about delegate like this:
The boss (secondVC) gives a the worker (firstVC) a protocol of what to do. The Worker reads the protocol and is doing the work with his function given in the protocol. Before the worker runs to the boss, he must be sure that he can do the next task (written on the protocol he is going to have.)

Related

Swift Image conversion failed from one view controller to another controller

I am trying to send the data in including image from one view controller to another . The data is fetching from API . The data is successfully loaded into first view controller including image but when I try to reuse same code with didSelectRow function I am getting following errors . Cannot assign value of type 'Data?' to type 'UIImage' . The error on this line dc.imagemovie = presenter.getImageData(by: row). Here is the code to fetch the data from API.
class MoviePresenter: MoviePresenterProtocol {
private let view: MovieViewProtocol
private let networkManager: NetworkManager
private var movies = [Movie]()
private var cache = [Int: Data]()
var rows: Int {
return movies.count
}
init(view: MovieViewProtocol, networkManager: NetworkManager = NetworkManager()) {
self.view = view
self.networkManager = networkManager
}
func getMovies() {
let url = "https://api.themoviedb.org/3/movie/popular?language=en-US&page=3&api_key=6622998c4ceac172a976a1136b204df4"
networkManager.getMovies(from: url) { [weak self] result in
switch result {
case .success(let response):
self?.movies = response.results
self?.downloadImages()
DispatchQueue.main.async {
self?.view.resfreshTableView()
}
case .failure(let error):
DispatchQueue.main.async {
self?.view.displayError(error.localizedDescription)
}
}
}
}
func getTitle(by row: Int) -> String? {
return movies[row].originalTitle
}
func getOverview(by row: Int) -> String? {
return movies[row].overview
}
func getImageData(by row: Int) -> Data? {
return cache[row]
}
private func downloadImages() {
let baseImageURL = "https://image.tmdb.org/t/p/w500"
let posterArray = movies.map { "\(baseImageURL)\($0.posterPath)" }
let group = DispatchGroup()
group.enter()
for (index, url) in posterArray.enumerated() {
networkManager.getImageData(from: url) { [weak self] data in
if let data = data {
self?.cache[index] = data
}
}
}
group.leave()
group.notify(queue: .main) { [weak self] in
self?.view.resfreshTableView()
}
}
}
Here is the code in view controller to display the data into table view cell .
class MovieViewController: UIViewController {
#IBOutlet weak var userName: UILabel!
#IBOutlet weak var tableView: UITableView!
private var presenter: MoviePresenter!
var finalname = ""
override func viewDidLoad() {
super.viewDidLoad()
userName.text = "Hello: " + finalname
setUpUI()
// configure presenter
presenter = MoviePresenter(view: self)
presenter.getMovies()
}
private func setUpUI() {
tableView.dataSource = self
tableView.delegate = self
}
#IBAction func selectSegment(_ sender: UISegmentedControl) {
if sender.selectedSegmentIndex == 0{
setUpUI()
presenter = MoviePresenter(view: self)
presenter.getMovies()
}
}
}
extension MovieViewController: MovieViewProtocol {
func resfreshTableView() {
tableView.reloadData()
}
func displayError(_ message: String) {
let alert = UIAlertController(title: "Error", message: message, preferredStyle: .alert)
let doneButton = UIAlertAction(title: "Done", style: .default, handler: nil)
alert.addAction(doneButton)
present(alert, animated: true, completion: nil)
}
}
extension MovieViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
presenter.rows
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: MovieViewCell.identifier, for: indexPath) as! MovieViewCell
let row = indexPath.row
let title = presenter.getTitle(by: row)
let overview = presenter.getOverview(by: row)
let data = presenter.getImageData(by: row)
cell.configureCell(title: title, overview: overview, data: data)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let dc = storyboard?.instantiateViewController(withIdentifier: "MovieDeatilsViewController") as! MovieDeatilsViewController
let row = indexPath.row
dc.titlemovie = presenter.getTitle(by: row) ?? ""
dc.overview = presenter.getOverview(by: row) ?? ""
**dc.imagemovie = presenter.getImageData(by: row) // Error on this line**
self.navigationController?.pushViewController(dc, animated: true)
}
}
/*
var titlemoview = ""
var overview = ""
var imagemovie = UIImage()
*/
extension MovieViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
}
Here is the code for display the data .
class MovieDeatilsViewController: UIViewController {
#IBOutlet weak var movieImage: UIImageView!
#IBOutlet weak var movieTitle: UILabel!
#IBOutlet weak var movieOverview: UILabel!
var titlemovie = ""
var overview = ""
var imagemovie = UIImage()
override func viewDidLoad() {
super.viewDidLoad()
movieTitle.text = titlemovie
movieOverview.text = overview
movieImage.image = imagemovie
}
}
Get UIImage from data in configureCell(:, : , :) function by UIImage(data:yourdata) and assign to your imageview
let image = UIImage(data:yourdata)
imageview.image = image

Populating fields in a custom tableView cell from data defined in a popover/popup

On the settings VC of my app I have a tableview with 5 different custom cells. The cell I’m needing some help with sets a default start and end time. This is accomplished using a popover/popup with a date/time control which is opened using a segue. This cell contains 2 buttons (start and end) and 2 fields (start and end). When one of the buttons is tapped the corresponding popover/popup is displayed. The user then defines the desired time and taps a done button. I’m using a protocol/delegate method to pass the time strings from the popover/popup. This currently works, with one exception. I want the newly defined time to populate the corresponding start or end field in he cell. My thought is to move all of this code into the custom tableViewCell where I have full access to the fields but I can’t seem to make that work. Moving the prepare(for segue: to the cell file isn’t recognized. Is there a way to perform this segue from the custom cell? Or does anybody have thoughts on populating the fields using my current setup? Thanks in advance for any help.
// All of this code is in the tableView controller except the last part where noted
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.identifier == "quote_Presets_StartTime" // # 2
{
let storyboard : UIStoryboard = UIStoryboard(name: K.StoryboardID.date_Picker_SB, bundle: nil)
var popupVC : Date_Popup_VC = storyboard.instantiateViewController(withIdentifier: K.StoryboardID.date_Popup_VC) as! Date_Popup_VC
let navigationController = UINavigationController(rootViewController: popupVC)
let deviceName = UIDevice().type
let deviceString: String = ("\(deviceName)")
if deviceString.contains(K.theDevice.iPad)
{
popupVC = segue.destination as! Date_Popup_VC
if ModelData.isInline() // Wheel
{
popupVC.preferredContentSize = CGSize(width: 320, height: 300)
} else { // Inline
popupVC.preferredContentSize = CGSize(width: 350, height: 150)
}
navigationController.isNavigationBarHidden = true
} else if deviceString.contains(K.theDevice.iPhone) {
present(navigationController, animated: true, completion: nil)
navigationController.isNavigationBarHidden = false
}
popupVC.theBtn = 2
let formattedTime = ModelData.formattedTime_Read(time_Db: new_Start_Time)
popupVC.theDate = formattedTime
popupVC.timeStart_Delegate = self
} else if segue.identifier == "quote_Presets_EndTime" { // # 3
let storyboard : UIStoryboard = UIStoryboard(name: K.StoryboardID.date_Picker_SB, bundle: nil)
var popupVC : Date_Popup_VC = storyboard.instantiateViewController(withIdentifier: K.StoryboardID.date_Popup_VC) as! Date_Popup_VC
let navigationController = UINavigationController(rootViewController: popupVC)
let deviceName = UIDevice().type
let deviceString: String = ("\(deviceName)")
if deviceString.contains(K.theDevice.iPad)
{
popupVC = segue.destination as! Date_Popup_VC
if ModelData.isInline() // Wheel
{
popupVC.preferredContentSize = CGSize(width: 320, height: 300)
} else { // Inline
popupVC.preferredContentSize = CGSize(width: 350, height: 150)
}
navigationController.isNavigationBarHidden = true
} else if deviceString.contains(K.theDevice.iPhone) {
present(navigationController, animated: true, completion: nil)
navigationController.isNavigationBarHidden = false
}
popupVC.theBtn = 3
let formattedTime = ModelData.formattedTime_Read(time_Db: new_End_Time)
popupVC.theDate = formattedTime
popupVC.timeEnd_Delegate = self
}
}
// Executed from the cellForRowAt code
#objc
func setTheTimes(sender: UIButton)
{
switch sender.tag
{
case 0:
performSegue(withIdentifier: "quote_Presets_StartTime", sender: Any?.self)
case 1:
performSegue(withIdentifier: "quote_Presets_EndTime", sender: Any?.self)
default: break
}
}
extension Quote_Presets_VC: UITableViewDelegate, UITableViewDataSource
{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
// cell_05 Time
let cell_05 = tableView.dequeueReusableCell(withIdentifier: K.ReuseIdentifier.defaultTime_Cell, for: indexPath) as! DefaultTime_Cell
cell_05.backgroundColor = Theme.current.darkAccentColor
cell_05.selectionStyle = .none
cell_05.delegate = self as SaveData_Time
cell_05.label_Outlet.labelTextColorOnly_Attributes(txt: K.Titles.defaultStartEnd, txtWgt: .regular)
cell_05.labelStart_Outlet.labelTextColorOnly_Attributes(txt: K.Titles.start, txtWgt: .regular)
cell_05.labelEnd_Outlet.labelTextColorOnly_Attributes(txt: K.Titles.end, txtWgt: .regular)
cell_05.startBtn_Outlet.addTarget(self, action: #selector(setTheTimes), for: .touchUpInside)
cell_05.endButton_Outlet.addTarget(self, action: #selector(setTheTimes), for: .touchUpInside)
cell_05.fldStart_Outlet.textField_Simple(text: ModelData.timeConversionTo_12(time24: new_Start_Time))
cell_05.fldEnd_Outlet.textField_Simple(text: ModelData.timeConversionTo_12(time24: new_End_Time))
switch indexPath.row
{
case 9: // Time
cell_05.fldStart_Outlet.tag = 0
cell_05.fldEnd_Outlet.tag = 1
cell_05.startBtn_Outlet.tag = 0
cell_05.endButton_Outlet.tag = 1
return cell_05
default: break
}
return UITableViewCell()
}
}
// These are the delegates for the protocol to pass the time string
extension Quote_Presets_VC: TimeStart_DatePopupDelegate, TimeEnd_DatePopupDelegate
{
func saveTimeStart(value: String)
{
var originalValue: String = ""
for theValue in presetsArray
{
originalValue = theValue.start_Time
}
let formattedValue = ModelData.timeConversionTo_24(time12: value)
if formattedValue != originalValue
{
new_Start_Time = value
save_Array_Quote[6] = K.AppFacing_Titles.true_
} else {
save_Array_Quote[6] = K.AppFacing_Titles.false_
}
setSaveBtn_QuotePresets()
}
func clearTimeStart(value: String)
{
// startFld_Outlet.text = value
}
func saveTimeEnd(value: String)
{
var originalValue: String = ""
for theValue in presetsArray
{
originalValue = theValue.end_Time
}
let formattedValue = ModelData.timeConversionTo_24(time12: value)
if formattedValue != originalValue
{
new_End_Time = value
save_Array_Quote[7] = K.AppFacing_Titles.true_
} else {
save_Array_Quote[7] = K.AppFacing_Titles.false_
}
setSaveBtn_QuotePresets()
}
func clearTimeEnd(value: String)
{
// endFld_Outlet.text = value
}
}
// The code below is the current tableview cell code
protocol SaveData_Time: AnyObject
{
func setSaveBtn_QuotePresets()
}
class DefaultTime_Cell: UITableViewCell
{
#IBOutlet weak var label_Outlet: UILabel!
#IBOutlet weak var labelStart_Outlet: UILabel!
#IBOutlet weak var fldStart_Outlet: UITextField!
#IBOutlet weak var fldEnd_Outlet: UITextField!
#IBOutlet weak var labelEnd_Outlet: UILabel!
#IBOutlet weak var startFldHgt_Constraint: NSLayoutConstraint!
#IBOutlet weak var startBtn_Outlet: UIButton!
#IBOutlet weak var endButton_Outlet: UIButton!
weak var delegate: SaveData_Time?
override func awakeFromNib()
{
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool)
{
super.setSelected(selected, animated: animated)
}
#IBAction func startBtn_Tapped(_ sender: UIButton)
{
// This throws an error that cannot find performSegue in scope
//performSegue(withIdentifier: "quote_Presets_StartTime", sender: Any?.self)
}
#IBAction func endBtn_Tapped(_ sender: UIButton)
{
// This throws an error that cannot find performSegue in scope
//performSegue(withIdentifier: "quote_Presets_EndTime", sender: Any?.self)
}
}
First of all, don't convert your dates into strings until you need to. Pass Date values around instead.
And, to answer your question about moving more logic into your cell, don't do that. Keep your views simple.
I'd just add two closures to your DateCell to indicate if a button was tapped:
class DateCell: UITableViewCell {
#IBOutlet weak var startDateButton: UIButton!
#IBOutlet weak var endDateButton: UIButton!
var didTapStart: () -> Void = {}
var didTapEnd: () -> Void = {}
#IBAction func didSelectStart(_ sender: UIButton) {
didTapStart()
}
#IBAction func didSelectEnd(_ sender: UIButton) {
didTapEnd()
}
}
In your date editor you can use a delegate or a closure to indicate that the date changed. I'll use a closure here:
class DateEditor: UIViewController {
var dateDidChange: (Date) -> Void = { _ in }
// ...
}
I'm not a fan of segues, but if you really want to use one, then create two segues, one for editing a start date and one for editing an end date:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let cell = sender as? UITableViewCell,
let row = tableView.indexPath(for: cell)?.row,
let vc = segue.destination as? DateEditor else { return }
if segue.identifier == "editStartDate" {
vc.date = dates[row].start
} else {
vc.date = dates[row].end
}
vc.dateDidChange = { [weak self] date in
if segue.identifier == "editStartDate" {
self?.dates[row].start = date
} else {
self?.dates[row].end = date
}
self?.tableView.reloadRows(at: [IndexPath(row: row, section: 0)], with: .fade)
}
}
Whenever one of the buttons was tapped, just trigger the segue:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "DateCell", for: indexPath) as! DateCell
cell.startDateButton.setTitle(
DateFormatter.awesome.string(from: self.dates[indexPath.row].start),
for: .normal
)
cell.endDateButton.setTitle(
DateFormatter.awesome.string(from: self.dates[indexPath.row].end),
for: .normal
)
cell.didTapStart = { [weak self] in
self?.performSegue(withIdentifier: "editStartDate", sender: cell)
}
cell.didTapEnd = { [weak self] in
self?.performSegue(withIdentifier: "editEndDate", sender: cell)
}
return cell
}
This is what I used for formatting dates:
extension DateFormatter {
static let awesome: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .short
formatter.timeStyle = .medium
return formatter
}()
}

Duplicated messages Using JSQMessagerViewController (Swift) (iOS)

I have a JSQMessagersViewController which is connected to a NavigationController. Moreover, I have a ViewController with a tableView with the contacts. Thus, when you click on a cell from the tableView, it opens the JSQMessagerViewController with that conversation. Inside the conversation, when the user sends a message the first time everything works fine. However, once the user goes out the conversation to the tableViewController, if he gets back in a conversation and sends a message, the message gets duplicated by the number of time the user when out and back in. In addition, once the message is duplicated and the user goes out of the conversation, once he click on the same conversation, the message is no longer duplicated. Indeed, when I verify on the database (Firebase), there is no duplicated messages. I can't figure out what is creating this loop.
MessageReceivedDelegate.swift
import Foundation
import Firebase
protocol MessageReceivedDelegate: class {
func message_received(senderID: String, senderName: String, text: String, target: String)
}
class messages_help {
private static let _instance = messages_help()
weak var delegate: MessageReceivedDelegate?
private var currentTarget = String()
static var Instance: messages_help {
return _instance
}
func sendMessage(senderID: String, senderName: String, text: String, target: String) {
let ref = FIRDatabase.database().reference(fromURL: )
let data: Dictionary <String, Any> = [Constants.sender_id: senderID, Constants.sender_name: senderName, Constants.text: text, Constants.target: target]
ref.child("messages").childByAutoId().setValue(data)
}
func getData() {
let ref = FIRDatabase.database().reference(fromURL: )
let user = FIRAuth.auth()?.currentUser
let curr = ref.child("messages")
curr.observe(.childAdded, with: {(snapshot) in
//print(snapshot)
//Get Value from DataBase
print()
if let data = snapshot.value as? NSDictionary {
if let senderID = data[Constants.sender_id] as? String{
if let senderName = data[Constants.sender_name] as? String {
if let target = data[Constants.target] as? String{
if user?.uid == senderID || user?.email == target{
if let text = data[Constants.text] as? String {
self.delegate?.message_received(senderID: senderID, senderName: senderName, text: text, target: target)
}
}
}
}
}
}
},withCancel: nil)
}
}
}
ChatViewController.swift
import UIKit
import JSQMessagesViewController
import MobileCoreServices
import AVKit
import FirebaseAuth
class ChatViewController: JSQMessagesViewController,
MessageReceivedDelegate {
#IBOutlet var targetLabel: UINavigationItem!
var messages = [JSQMessage]()
#IBOutlet var chatLabel: UINavigationItem!
var i = 0
var targetEmail = String()
override func viewDidLoad() {
super.viewDidLoad()
let user = FIRAuth.auth()?.currentUser
messages_help.Instance.delegate = self
self.senderId = user?.uid
self.senderDisplayName = user?.email
self.targetLabel.title = targetEmail
setupBackButton()
// Show Button to simulate incoming messages
self.inputToolbar.contentView.leftBarButtonItem = nil
if Language().check_language() == "Fr"{
self.inputToolbar.contentView.textView.placeHolder = "Nouveau message";
}
else if Language().check_language() == "Es"{
self.inputToolbar.contentView.textView.placeHolder = "Nuevo mensaje";
}
self.inputToolbar.contentView.rightBarButtonItem.setImage(UIImage(named: "paper_plane"), for: .normal)
self.inputToolbar.contentView.rightBarButtonItem.setTitleColor(UIColor.brown, for: .normal)
automaticallyScrollsToMostRecentMessage = true
//self.collectionView?.reloadData()
//messages_help.Instance.getData()
// Do any additional setup after loading the view.
messages.removeAll()
messages_help.Instance.getData()
}
func go() {
let toViewController = storyboard?.instantiateViewController(withIdentifier: "previous") as! PreviousRequestsViewController
//Go to the page
self.present(toViewController, animated:true, completion: nil)
}
//message received
func message_received(senderID: String, senderName: String, text: String, target: String) {
if target == targetEmail || senderName == targetEmail {
//print("Number of loop\n")
//i = i+1
// print(i)
/**
* Scroll to actually view the indicator
*/
self.scrollToBottom(animated: true)
messages.append(JSQMessage(senderId: senderID, displayName: senderName, text: text))
}
collectionView.reloadData()
}
// Number of rows
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return messages.count
}
// Cell
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = super.collectionView(collectionView, cellForItemAt: indexPath) as! JSQMessagesCollectionViewCell
return cell
}
// Display messages
override func collectionView(_ collectionView: JSQMessagesCollectionView!, messageDataForItemAt indexPath: IndexPath!) -> JSQMessageData! {
return messages[indexPath.item]
}
// Avatar
override func collectionView(_ collectionView: JSQMessagesCollectionView!, avatarImageDataForItemAt indexPath: IndexPath!) -> JSQMessageAvatarImageDataSource! {
return JSQMessagesAvatarImageFactory.avatarImage(with: UIImage(named: "iTunesArtwork"), diameter: 30)
}
//the bubble
override func collectionView(_ collectionView: JSQMessagesCollectionView!, messageBubbleImageDataForItemAt indexPath: IndexPath!) -> JSQMessageBubbleImageDataSource! {
let bubble_fact = JSQMessagesBubbleImageFactory()
let message = messages[indexPath.item]
if message.senderId == self.senderId {
return bubble_fact?.outgoingMessagesBubbleImage(with: UIColor.brown)
}
else {
return bubble_fact?.incomingMessagesBubbleImage(with: UIColor.darkGray)
}
}
// When pressed sent
override func didPressSend(_ button: UIButton!, withMessageText text: String!, senderId: String!, senderDisplayName: String!, date: Date!) {
messages_help.Instance.sendMessage(senderID: senderId, senderName: senderDisplayName, text: text, target: self.targetEmail )
//dismiss text from text fild
finishSendingMessage()
}
func setupBackButton() {
if Language().check_language() == "Fr"{
let backButton = UIBarButtonItem(title: "Retour", style: UIBarButtonItemStyle.plain, target: self, action: #selector(backButtonTapped))
backButton.tintColor = UIColor.brown
navigationItem.leftBarButtonItem = backButton
}
else if Language().check_language() == "Es"{
let backButton = UIBarButtonItem(title: "Regresa", style: UIBarButtonItemStyle.plain, target: self, action: #selector(backButtonTapped))
backButton.tintColor = UIColor.brown
navigationItem.leftBarButtonItem = backButton
} else {
let backButton = UIBarButtonItem(title: "Back", style: UIBarButtonItemStyle.plain, target: self, action: #selector(backButtonTapped))
backButton.tintColor = UIColor.brown
navigationItem.leftBarButtonItem = backButton
}
}
func backButtonTapped() {
self.dismiss(animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
}
ContactChatViewController.swift
import UIKit
import Firebase
class ContactChatViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate {
#IBOutlet var chat_TableView: UITableView!
var ref: FIRDatabaseReference!
var publish = [String?]()
var data_to_send = String()
var encountered = Set<String>()
override func viewDidLoad() {
super.viewDidLoad()
ref = FIRDatabase.database().reference(fromURL: )
getDataa()
}
// get data from trips
func getDataa() {
// Reference to current user
let user = FIRAuth.auth()?.currentUser
let curr = ref.child("messages")
curr.observe(.childAdded, with: {(snapshot) in
if let data = snapshot.value as? [String: AnyObject] {
let use = messages()
use.setValuesForKeys(data)
//Append new data if same user
if user?.email == use.target || user?.email == use.sender_name {
if self.encountered.contains(use.sender_name!) || (user?.email)! == use.sender_name! {
//print("Already inside")
} else {
self.encountered.insert(use.sender_name!)
self.publish.append(use.sender_name)
}
//Appen to to array even if current user sent a message but is not the target
if user?.email == use.sender_name{
if self.encountered.contains(use.target!) {
print("Already inside")
} else {
self.encountered.insert(use.target!)
self.publish.append(use.target)
}
}
}
//Load data in U Thread
DispatchQueue.main.async {
self.chat_TableView.reloadData()
}
}
}
,withCancel: nil)
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
// hide separators
tableView.separatorStyle = .none
//return count with database
return publish.count
}
// Assign rows a value
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell_user = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "previous_cell")
cell_user.selectionStyle = .none
cell_user.backgroundColor = UIColor.clear
cell_user.textLabel?.textColor = UIColor.brown
cell_user.textLabel?.font = UIFont.systemFont(ofSize: 25)
// Check for duplicates
cell_user.textLabel?.text = self.publish[indexPath.row]
return cell_user
}
//Clicked on a cell
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
data_to_send = self.publish[indexPath.row]!
self.performSegue(withIdentifier: "chatSegue", sender: self)
//let chatView = ChatViewController()
//chatView.targetEmail = self.publish[indexPath.row]!
//let chatNavigationController = UINavigationController(rootViewController: chatView)
//present(chatNavigationController, animated: true, completion: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//let toViewController = storyboard?.instantiateViewController(withIdentifier: "chatPage") as! ChatViewController
//toViewController.targetEmail = data_to_send
//Go to the page
//self.present(toViewController, animated:true, completion: nil)
let navVC = segue.destination as? UINavigationController
let chatVC = navVC?.viewControllers.first as! ChatViewController
chatVC.targetEmail = data_to_send
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}

Bar Button Nil After Pressing Switch in Swift?

Ok here is what is going on. I have a table view class called MainTabeViewController. I have a sidebar class called SettingsSidebarViewController that uses SW Reveal to show a menu. The menu is toggled by a bar button item called settings. The menu works fine with the bar button item, and when you press it the menu toggles like it should.
However, once I click a switch, the app crashes and I start getting a EXC_BAD_INSTRUCTION error that reads in the console Fatal error: unexpectedly found nil while unwrapping an optional value. Why is the bar button item suddenly nil after the switch is pressed?
MAINTABLEVIEWCONTROLLER.swift
import UIKit
import SwiftyJSON
class MainTableViewController: UITableViewController, SettingsSidebarViewDelegate {
//HEERE IS THE BAR BUTTON ITEM CALLED SETTINGS <- <- <-
#IBOutlet var settings: UIBarButtonItem!
var NumberofRows = 0
var names = [String]()
var descriptions = [String]()
var categories = [String]()
var types = [String]()
var series = [String]()
var groups = [String]()
func parseJSON(){
let path = NSBundle.mainBundle().URLForResource("documents", withExtension: "json")
let data = NSData(contentsOfURL: path!) as NSData!
let readableJSON = JSON(data: data)
NumberofRows = readableJSON["Documents"].count
for i in 1...NumberofRows {
let doc = "Doc" + "\(i)"
let Name = readableJSON["Documents"][doc]["name"].string as String!
let Description = readableJSON["Documents"][doc]["description"].string as String!
let Category = readableJSON["Documents"][doc]["category"].string as String!
let Type = readableJSON["Documents"][doc]["type"].string as String!
let Series = readableJSON["Documents"][doc]["tags"]["series"].string as String!
let Group = readableJSON["Documents"][doc]["tags"]["group"].string as String!
names.append(Name)
descriptions.append(Description)
categories.append(Category)
types.append(Type)
series.append(Series)
groups.append(Group)
}
}
Here is where the errors start to occur AFTER the switch is pressed (still in same class)
func initSettings(){
//Sets button title to gear, sets button actions (to open menu)
settings.title = NSString(string: "\u{2699}\u{0000FE0E}") as String!
let font = UIFont.systemFontOfSize(25);
settings.setTitleTextAttributes([NSFontAttributeName: font], forState:UIControlState.Normal)
settings.target = self.revealViewController()
settings.action = #selector(SWRevealViewController.rightRevealToggle(_:))
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
func showTags(showTags: Bool) {
tableView.reloadData()
}
func showTimestamp(showTimeStamp: Bool) {
tableView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
parseJSON()
initSettings()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return NumberofRows
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("MainTableCell", forIndexPath: indexPath) as! MainTableViewCell
if names.count != 0{
cell.fileName.text = names[indexPath.row]
cell.fileDescription.text = descriptions[indexPath.row]
cell.fileCategory.text = categories[indexPath.row]
cell.fileType.text = types[indexPath.row]
cell.options.setTitle(NSString(string: ":") as String!, forState: .Normal)
cell.tag1.text = series[indexPath.row]
cell.tag2.text = groups[indexPath.row]
if showTagsVal{
cell.tag1.hidden = false
}
else{
cell.tag1.hidden = true
}
if showTimeStampVal{
cell.tag2.hidden = false
}
else{
cell.tag2.hidden = true
}
}
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("showView", sender: self)
}
// MARK: - Navigation
//In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "showView"){
let detailVC: DetailViewController = segue.destinationViewController as! DetailViewController
let indexPath = self.tableView.indexPathForSelectedRow!
detailVC.text = names[indexPath.row]
self.tableView.deselectRowAtIndexPath(indexPath, animated: true)
}}}
SettingsSidebarViewController.swift
import UIKit
protocol SettingsSidebarViewDelegate{
func showTags(showTags: Bool);
func showTimestamp(showTimeStamp: Bool)
}
var showTagsVal = false
var showTimeStampVal = false
class SettingsSidebarViewController: UIViewController {
var delegate: SettingsSidebarViewDelegate! = nil
#IBOutlet weak var sidebar_title: UILabel!
#IBOutlet var showTagsSwitch: UISwitch!
#IBOutlet var showTimestampSwitch: UISwitch!
#IBAction func switchPressed(sender: AnyObject) {
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let nextViewController = storyBoard.instantiateViewControllerWithIdentifier("main") as! MainTableViewController
self.presentViewController(nextViewController, animated:true, completion:nil)
let vc = MainTableViewController()
self.delegate = vc
if showTagsSwitch.on{
showTagsVal = true
delegate.showTags(showTagsVal)
}
else{
showTagsVal = false
delegate.showTags(showTagsVal)
}
if showTimestampSwitch.on{
showTimeStampVal = true
delegate.showTimestamp(showTimeStampVal)
}
else{
showTimeStampVal = false
delegate.showTimestamp(showTimeStampVal)
}
}
override func viewDidLoad() {
super.viewDidLoad()
sidebar_title.text = "Settings"
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
Help is appreciated! I am sure this is a question concerning transitioning view controllers that is something easy but I have tried too long to figure it out.
Your problem is you are creating a new instance of MainTableViewController and assigning it to delegate. That's why the bar button item is nil, because all the initialization and binding isn't done.
You have to change the delegate and assign the view controller you already got with instantiateViewControllerWithIdentifier:
self.delegate = nextViewController

Swift code. fatal error: unexpectedly found nil while unwrapping an Optional value

When I run my app I get an error message that says
fatal error: unexpectedly found nil while unwrapping an Optional value
MealViewController.swift
import UIKit
class MealViewController: UIViewController, UITextFieldDelegate, UINavigationControllerDelegate {
// MARK: Properties
#IBOutlet weak var nameTextField: UITextField!
#IBOutlet weak var saveButton: UIBarButtonItem!
/*
This value is either passed by `MealTableViewController` in `prepareForSegue(_:sender:)`
or constructed as part of adding a new meal.
*/
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if saveButton === sender {
let weightInt: Int? = Int(weightInKilos.text!)
let dehydrationInt: Int? = Int(percentOfDehydration.text!)
let lossesInt: Int? = Int(ongoingLosses.text!)
let factorInt: Int? = Int(Factor.text!)
let name = nameTextField.text ?? ""
let wt = weightInt!
let dn = dehydrationInt!
let ol = lossesInt!
let fr = factorInt!
// Set the meal to be passed to MealListTableViewController after the unwind segue.
meal = Meal(name: name, wt: wt, dn: dn, ol: ol, fr: fr)
}
if calcButton === sender {
if weightInKilos.text == "" && percentOfDehydration.text == "" && ongoingLosses.text == "" && Factor.text == "" {
let alertController = UIAlertController(title: "Fields were left empty.", message:
"You left some fields blank! Please make sure that all fields are filled in before tapping 'Calculate'.", preferredStyle: UIAlertControllerStyle.Alert)
alertController.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default,handler: nil))
self.presentViewController(alertController, animated: true, completion: nil)
}
else {
let DestViewController: ftrViewController = segue.destinationViewController as! ftrViewController
let weightInt: Int? = Int(weightInKilos.text!)
let dehydrationInt: Int? = Int(percentOfDehydration.text!)
let lossesInt: Int? = Int(ongoingLosses.text!)
let factorInt: Int? = Int(Factor.text!)
let lrs24Int = (30 * weightInt! + 70) * factorInt! + weightInt! * dehydrationInt! * 10 + lossesInt!
let lrsPerHourint = lrs24Int / 24
DestViewController.lrsHr = "\(lrsPerHourint)"
DestViewController.lrs24Hrs = "\(lrs24Int)"
}
}
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
var meal: Meal?
override func viewDidLoad() {
super.viewDidLoad()
// Handle the text field’s user input through delegate callbacks.
nameTextField.delegate = self
// Set up views if editing an existing Meal.
if let meal = meal {
navigationItem.title = meal.name
nameTextField.text = meal.name
var weightInt: Int? = Int(weightInKilos.text!)
var dehydrationInt: Int? = Int(percentOfDehydration.text!)
var lossesInt: Int? = Int(ongoingLosses.text!)
var factorInt: Int? = Int(Factor.text!)
weightInt = meal.wt
dehydrationInt = meal.dn
lossesInt = meal.ol
factorInt = meal.fr
weightInKilos.delegate = self
percentOfDehydration.delegate = self
ongoingLosses.delegate = self
Factor.delegate = self
calcButton.layer.cornerRadius = 4;
resetbutton.layer.cornerRadius = 4;
}
// Enable the Save button only if the text field has a valid Meal name.
checkValidMealName()
}
// MARK: UITextFieldDelegate
func textFieldDidEndEditing(textField: UITextField) {
checkValidMealName()
navigationItem.title = textField.text
}
func textFieldDidBeginEditing(textField: UITextField) {
// Disable the Save button while editing.
saveButton.enabled = false
}
func checkValidMealName() {
// Disable the Save button if the text field is empty.
let text = nameTextField.text ?? ""
saveButton.enabled = !text.isEmpty
}
// MARK: Navigation
#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 isPresentingInAddMealMode = presentingViewController is UINavigationController
if isPresentingInAddMealMode {
dismissViewControllerAnimated(true, completion: nil)
} else {
navigationController!.popViewControllerAnimated(true)
}
}
// Calculation outlets & actions
#IBOutlet weak var resetbutton: UIButton!
#IBOutlet weak var calcButton: UIButton!
#IBOutlet weak var weightInKilos: UITextField!
#IBOutlet weak var percentOfDehydration: UITextField!
#IBOutlet weak var ongoingLosses: UITextField!
#IBOutlet weak var Factor: UITextField!
#IBAction func resetButton(sender: AnyObject) {
if weightInKilos.text == "" && percentOfDehydration.text == "" && ongoingLosses.text == "" && Factor.text == "" {
let alertController = UIAlertController(title: "...", message:
"There is no information to reset. Nice try though!", preferredStyle: UIAlertControllerStyle.Alert)
alertController.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default,handler: nil))
self.presentViewController(alertController, animated: true, completion: nil)
}
else {
weightInKilos.text=""
percentOfDehydration.text=""
ongoingLosses.text=""
Factor.text=""
}
}
}
class ftrViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var lrs24hours: UILabel!
#IBOutlet weak var lrsPerHour: UILabel!
var lrs24Hrs = String()
var lrsHr = String()
override func viewDidLoad() {
lrs24hours.text = lrs24Hrs
lrsPerHour.text = lrsHr
}
}
Meal.swift
import UIKit
class Meal: NSObject, NSCoding {
// MARK: Properties
var name: String
var wt: Int
var dn: Int
var ol: Int
var fr: Int
// MARK: Archiving Paths
static let DocumentsDirectory = NSFileManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!
static let ArchiveURL = DocumentsDirectory.URLByAppendingPathComponent("meals")
// MARK: Types
struct PropertyKey {
static let nameKey = "name"
static let wtKey = "weight"
static let dnKey = "dehydration"
static let olKey = "ongoinglosses"
static let frKey = "factor"
}
// MARK: Initialization
init?(name: String, wt: Int, dn: Int, ol: Int, fr: Int) {
// Initialize stored properties.
self.name = name
self.wt = wt
self.dn = dn
self.ol = ol
self.fr = fr
super.init()
// Initialization should fail if there is no name or if the rating is negative.
if name.isEmpty {
return nil
}
}
// MARK: NSCoding
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(name, forKey: PropertyKey.nameKey)
aCoder.encodeObject(wt, forKey: PropertyKey.wtKey)
aCoder.encodeObject(dn, forKey: PropertyKey.dnKey)
aCoder.encodeObject(ol, forKey: PropertyKey.olKey)
aCoder.encodeObject(fr, forKey: PropertyKey.frKey)
}
required convenience init?(coder aDecoder: NSCoder) {
let name = aDecoder.decodeObjectForKey(PropertyKey.nameKey) as! String
let wt = aDecoder.decodeObjectForKey(PropertyKey.wtKey) as! Int
let dn = aDecoder.decodeObjectForKey(PropertyKey.dnKey) as! Int
let ol = aDecoder.decodeObjectForKey(PropertyKey.olKey) as! Int
let fr = aDecoder.decodeObjectForKey(PropertyKey.frKey) as! Int
// Must call designated initializer.
self.init(name: name, wt: wt, dn: dn, ol: ol, fr: fr)!
}
}
MealTableViewController.swift
import UIKit
class MealTableViewController: UITableViewController {
// MARK: Properties
var meals = [Meal]()
override func viewDidLoad() {
super.viewDidLoad()
// Use the edit button item provided by the table view controller.
navigationItem.leftBarButtonItem = editButtonItem()
// Load any saved meals, otherwise load sample data.
if let savedMeals = loadMeals() {
meals += savedMeals
} else {
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return meals.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Table view cells are reused and should be dequeued using a cell identifier.
let cellIdentifier = "MealTableViewCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! MealTableViewCell
// Fetches the appropriate meal for the data source layout.
let meal = meals[indexPath.row]
cell.nameLabel.text = meal.name
return cell
}
// Override to support conditional editing of the table view.
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
// Override to support editing the table view.
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
// Delete the row from the data source
meals.removeAtIndex(indexPath.row)
saveMeals()
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
} else if editingStyle == .Insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
/*
// Override to support rearranging the table view.
override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
}
*/
/*
// Override to support conditional rearranging of the table view.
override func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the item to be re-orderable.
return true
}
*/
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowDetail" {
let mealDetailViewController = segue.destinationViewController as! MealViewController
// Get the cell that generated this segue.
if let selectedMealCell = sender as? MealTableViewCell {
let indexPath = tableView.indexPathForCell(selectedMealCell)!
let selectedMeal = meals[indexPath.row]
mealDetailViewController.meal = selectedMeal
}
}
else if segue.identifier == "AddItem" {
print("Adding new meal.")
}
}
#IBAction func unwindToMealList(sender: UIStoryboardSegue) {
if let sourceViewController = sender.sourceViewController as? MealViewController, meal = sourceViewController.meal {
if let selectedIndexPath = tableView.indexPathForSelectedRow {
// Update an existing meal.
meals[selectedIndexPath.row] = meal
tableView.reloadRowsAtIndexPaths([selectedIndexPath], withRowAnimation: .None)
} else {
// Add a new meal.
let newIndexPath = NSIndexPath(forRow: meals.count, inSection: 0)
meals.append(meal)
tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Bottom)
}
// Save the meals.
saveMeals()
}
}
// MARK: NSCoding
func saveMeals() {
let isSuccessfulSave = NSKeyedArchiver.archiveRootObject(meals, toFile: Meal.ArchiveURL.path!)
if !isSuccessfulSave {
print("Failed to save meals...")
}
}
func loadMeals() -> [Meal]? {
return NSKeyedUnarchiver.unarchiveObjectWithFile(Meal.ArchiveURL.path!) as! [Meal]
}
}
MealTableViewCell.swift
import UIKit
class MealTableViewCell: UITableViewCell {
// MARK: Properties
#IBOutlet weak var nameLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
try this convenience required init?(coder aDecoder: NSCoder) method
convenience required init?(coder aDecoder: NSCoder) {
if let name = aDecoder.decodeObjectForKey(PropertyKey.nameKey) as? String,
wt = aDecoder.decodeObjectForKey(PropertyKey.wtKey) as? Int,
dn = aDecoder.decodeObjectForKey(PropertyKey.dnKey) as? Int,
ol = aDecoder.decodeObjectForKey(PropertyKey.olKey) as? Int,
fr = aDecoder.decodeObjectForKey(PropertyKey.frKey) as? Int {
// Must call designated initializer.
self.init(name: name, wt: wt, dn: dn, ol: ol, fr: fr)
} else {
return nil
}
}

Resources