Preserve data in table from a View to another - ios

I'm trying to send values from one view to other and print them in a table as an array. The program work and display the data but the problem is that when I try to add another value to the table when I return to the view that have the table the previous values are no longer there.
In this segment of code I sent the data to the other view
import UIKit
class NewContactoViewController: UIViewController, UITextFieldDelegate {
var contacto: String = ""
var numero: String = ""
#IBOutlet weak var contactoField: UITextField!
#IBOutlet weak var numField: UITextField!
let defaultValues = UserDefaults.standard
#IBAction func addButton(_ sender: UIButton) {
contacto = contactoField.text!
numero = numField.text!
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
var secondController = segue.destination as! ContactosViewController
secondController.contactos = contactoField.text!
secondController.numerosmov = numField.text!
}
override func viewDidLoad() {
super.viewDidLoad()
self.contactoField.delegate = self
self.numField.delegate = self
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
In this segment of code are the tableviews that display the data
import UIKit
class ContactosViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
let defaultValues = UserDefaults.standard
var contactos: String = ""
var numerosmov: String = ""
var tablacontacto = [String] ()
var tablanumero = [String] ()
let cellIdentifier: String = "cell"
let cellIdentifier2: String = "cell2"
#IBOutlet weak var contactoTable: UITableView!
#IBOutlet weak var numTable: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
let backButton = UIBarButtonItem(title: "", style: UIBarButtonItemStyle.plain, target: navigationController, action: nil)
navigationItem.leftBarButtonItem = backButton
// Do any additional setup after loading the view.
datosRecividos(contactos, numerosmov)
contactoTable.delegate = self
numTable.delegate = self
contactoTable.dataSource = self
numTable.dataSource = self
contactoTable!.reloadData()
numTable!.reloadData()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
contactoTable.reloadData()
numTable.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func datosRecividos(_ contactosr: String, _ numerosr: String)
{
tablacontacto.append(contactosr)
tablanumero.append(numerosr)
let usercontacto = defaultValues.array(forKey: "contactoTable")
let usernumero = defaultValues.array(forKey: "numeroTable")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (tableView.tag == 1)
{
return(tablacontacto.count)
}
else if (tableView.tag == 2)
{
return(tablanumero.count)
}
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)
if (tableView.tag == 1)
{
cell.textLabel?.text = tablacontacto[indexPath.row] as! String
}
else if (tableView.tag == 2)
{
cell.textLabel?.text = tablanumero[indexPath.row] as! String
}
return(cell)
}
}

There are several things wrong here, I would suggest reading again about tableView's (especially the "Load Initial Data" section) -
https://developer.apple.com/library/content/referencelibrary/GettingStarted/DevelopiOSAppsSwift/CreateATableView.html
Your tables are getting data from the "tablacontacto" and "tablanumero" arrays.
There is no place in the code you sent to populate these arrays. (Do you see anything when these tables are on screen?)
Plus You are updating these arrays with only in the "func datosRecividos(_ contactosr: String, _ numerosr: String)" -
This method is only called once in the viewDidLoad and it is not called when you segue back to this screen
Plus you have no place you get data to your arrays from your userDefaults and there is no place you save the "new" data from "NewContactoViewController" to your userDefaults.

Make this two variables as static variables
internal static var CONTACTTO: String = ""
internal static var NUMERO: String = ""
then access this variables using,
NewContactoViewController.CONTACTTO
NewContactoViewController.NUMERO
Then there is no need sending this values using
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
var secondController = segue.destination as! ContactosViewController
secondController.contactos = contactoField.text!
secondController.numerosmov = numField.text!
}
Or otherwise you can save these values in shared preferences

You can declare a delegate for data changing in NewContactoViewController
import UIKit
//Create a delegate for data changing
protocol ContactChageDelegate: class {
func contactChanged(newContact: String, newNumber: String)
}
class NewContactoViewController: UIViewController, UITextFieldDelegate {
//declare delegate variable
weak var contactChageDelegate: ContactChageDelegate?
...
#IBAction func addButton(_ sender: UIButton) {
contacto = contactoField.text!
numero = numField.text!
//if need notify data changing. maybe it will not change
//if data change {
self.contactChageDelegate.contactChanged(newContact: contacto, newNumber: numero)
//}
}
and use this delegate in ContactosViewController like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//let self to delegate
if let newContactoViewController = segue.destination as? NewContactoViewController {
newContactoViewController.contactChageDelegate = self
}
}
...
//implement delegate method
extension ContactosViewController: ContactChageDelegate {
internal func contactChanged(newContact: String, newNumber: String) {
//now you have new values
//change your data array
//and reload table
}
}

Related

how to perform segue to a VC with Container

see this gif
when I choose the city Med , it passed to the TableVC not to the FirstVC (MainVC)
can I do that ? segue to the mainVC with the data passed through
the container (TableVC) ?
here what I did so far
MainVC
Empty
TableVC
import UIKit
class passedViewController: UITableViewController {
#IBOutlet weak var passcelltow: UITableViewCell!
#IBOutlet weak var passcell: UITableViewCell!
var passedCity1 = "اختر المدينة الاولى"
var passedCity2 = "اختر المدينة الثانية"
override func viewDidLoad() {
super .viewDidLoad()
passcell.textLabel?.text = passedCity1
passcelltow.textLabel?.text = passedCity2
}
}
Table 1 with data to pass to the TableVC
import UIKit
class city2ViewController: UIViewController , UITableViewDelegate , UITableViewDataSource{
#IBOutlet weak var tableView: UITableView!
var city2 = ["RUH" , "Med" , "Jed"]
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return city2.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
print(indexPath.row)
cell.textLabel?.text = city2[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "show", sender: city2[indexPath.row])
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let passing = segue.destination as! passedViewController
passing.passedCity2 = sender as! String
}
}
Table 2 is the same ..
commend error
0 1 2 Could not cast value of type 'UIViewController' (0x107a10288) to
'table_view_test_pass.passedViewController' (0x105dbfdf8). (lldb)
You can pass data via segues or protocols. Since you are using segues i will show you a complete example and how to do it the right way in Swift 3. Using only two ViewControllers.
Create two UITextFields in the main "ViewController".
Create a new view controller of type UIViewController call it "MainTabelViewController" and add a tableView in it. Select content Dynamic prototypes Style Grouped and create 1 prototype cell and add a UILabel to it for the city name. "Don't forget the put the cell identifier name". I called it "cell".
Add the delegates and data sources to the class and add its functions like in code.
Create a segue from the main view controller to the main table view controller. And create another segue the opposite direction. "Don't forget the put the segue identifier names" I called them "toCity" & "toMain"
Create a "CityTableViewCell" controller of type UITableViewCell and create an IBOutlet of UILabel type where you will save the city name in as a text.
Edit this part in the AppDelegate.swift To delete the city names saved using in the UserDefaults every time the app is launched. So i wont populate the UITextFields randomly every time.
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var userDefaults: UserDefaults!
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
userDefaults = UserDefaults.standard
userDefaults.removeObject(forKey: "City One")
userDefaults.removeObject(forKey: "City Two")
return true
}
This is the ordinary main ViewController.swift where you have your UITextFields in. I distinguish which UITextField did the user click on using the tags. You need to add also the UITextFieldDelegate protocol to be able to use the the textFieldDidBeginEditing function. And i also save the selected city names using UserDefaults class to call them when user chooses the other city.
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet var cityOneLabel: UITextField!
#IBOutlet var cityTwoLabel: UITextField!
#IBOutlet var continueButton: UIButton!
var selectedCityOne = ""
var selectedCityTwo = ""
var userDefaults: UserDefaults!
override func viewDidLoad() {
super.viewDidLoad()
cityOneLabel.delegate = self
cityTwoLabel.delegate = self
cityOneLabel.tag = 1
cityTwoLabel.tag = 2
continueButton.isEnabled = false
}
override func viewDidAppear(_ animated: Bool) {
userDefaults = UserDefaults.standard
cityOneLabel.text = selectedCityOne
cityTwoLabel.text = selectedCityTwo
if selectedCityOne != "" {
userDefaults.set(selectedCityOne, forKey: "City One")
} else {
cityOneLabel.text = userDefaults.string(forKey: "City One")
}
if selectedCityTwo != "" {
userDefaults.set(selectedCityTwo, forKey: "City Two")
} else {
cityTwoLabel.text = userDefaults.string(forKey: "City Two")
}
if cityOneLabel.text != "" && cityTwoLabel.text != "" {
continueButton.isEnabled = true
} else {
continueButton.isEnabled = false
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func continueButtonAction(_ sender: UIButton) {
//Later on continue after selecting the cities
}
func textFieldDidBeginEditing(_ textField: UITextField) {
performSegue(withIdentifier: "toCity", sender: textField.tag)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toCity" {
guard let cityVC = segue.destination as? MainTableViewController else {
return
}
cityVC.selectedTextField = sender as! Int
}
}
}
In the CityTabelViewCell.swift add the IBOutlet UILabel for the city name.
import UIKit
class CityTableViewCell: UITableViewCell {
#IBOutlet var cityNameLabel: 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
}
}
For the MainTabelViewController.swift write this:
Here is where i create an array of strings to populate my table view UILabels with.
import UIKit
class MainTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var cityTabelView: UITableView!
var cityNamesArray = ["Cairo", "Alexandria", "Suez"]
var selectedTextField = Int()
var selectedCityName = ""
override func viewDidLoad() {
super.viewDidLoad()
cityTabelView.delegate = self
cityTabelView.dataSource = self
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CityTableViewCell
cell.cityNameLabel.text = cityNamesArray[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cityNamesArray.count
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedCityName = cityNamesArray[indexPath.row]
performSegue(withIdentifier: "toMain", sender: self)
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
var title = ""
if selectedTextField == 1 {
title = "City One"
} else if selectedTextField == 2 {
title = "City Two"
}
return title
}
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toMain" {
guard let mainVC = segue.destination as? ViewController else {
return
}
if selectedTextField == 1 {
mainVC.selectedCityOne = selectedCityName
} else if selectedTextField == 2 {
mainVC.selectedCityTwo = selectedCityName
}
}
}
}
This is how my layout looks like. Try it. I just added a continue button too if the user will have to go to another UIViewController after selecting the two cities.
If you want to segue to MainVC, you should instantiate a view controller from that class in prepare for segue.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let passing = segue.destination as! ViewController
passing.passedCity2 = sender as! String
}
Change ViewController to whatever the name of your class is for MainVC.
If you want to go back to the Parent View, you should be using an unwind-segue.
For that you must create the unwind segue method in the Parent View like this
#IBAction func unwindSegueFromChild(segue: UIStoryboardSegue){
// This code executes when returning to view
}
And in your child view you must create the unwind segue ctrl+dragging
There a dropdown appears and you select unwindSegueFromChild
Once you've done that, you must assign the unwind segue an identifier and programmatically perform it like a normal segue.

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

Why is my tableViewController not loading any data?

Im creating an app where different buttons in a ViewController load different menu's into the tableViewController. The buttons are linked by a prepare for segue and the menu's (arrays) are linked by a contentMode. 1: breakfast menu & 2: lunch menu. I had allot of help from someone setting this up but now the table is not loading any data... The cell has 3 labels which display an item, info and price. It changes value within the code when a contentMode is selected. Does anyone see the problem in my code? thanks a lot!
import UIKit
class foodMenuController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let foodMenuController = segue.destinationViewController as! foodTableViewController
if segue.identifier == "showBreakfast" {
foodMenuController.contentMode = 1
} else if segue.identifier == "showLunch" {
foodMenuController.contentMode = 2
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
import UIKit
class foodTableViewCell: UITableViewCell {
#IBOutlet weak var foodItem: UILabel!
#IBOutlet weak var foodDescription: UILabel!
#IBOutlet weak var foodPrice: 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
}
}
import UIKit
class foodTableViewController: UITableViewController {
//Content Mode Selection in menu
var contentMode = 0
// THIS SHOULD BE LOADED WHEN CONTENT MODE is "1" --> BREAKFAST
let breakfastItems = ["Bread", "Coffee", "Nada"]
let breakfastInfo = ["Good", "Nice", "Nothing"]
let breakfastPrice = ["$1", "$100", "$12,40"]
// THIS SHOULD BE LOADED WHEN CONTENT MODE IS "2" --> LUNCH
let lunchItems = ["Not bread", "Not Coffee", "Something"]
let lunchInfo = ["Not good", "Not nice", "Yes"]
let lunchPrice = ["$1", "$100", "$12,40"]
var foodItems: [String] = []
var foodInfo: [String] = []
var foodPrice: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
switch (contentMode){
case 1: contentMode = 1
foodItems = breakfastItems
foodInfo = breakfastInfo
foodPrice = breakfastPrice
case 2: contentMode = 2
foodItems = lunchItems
foodInfo = lunchInfo
foodPrice = lunchPrice
default:
break
}
tableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section:
Int) -> Int {
return foodItems.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "Cell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! foodTableViewCell
cell.foodItem.text = foodItems[indexPath.row]
cell.foodDescription.text = foodInfo[indexPath.row]
cell.foodPrice.text = foodPrice[indexPath.row]
return cell
}
}
There isn't anything apparently wrong with the snippet you shared. You can check what is returned in the tableView:numberOfRowsInSection: method and see if it is returning a value > 0
Also, this is a given but we've all done it at some point of time - check to make sure the tableview delegate and datasource are set to your viewcontroller.
I have made slight modifications in your project.
1. make the UINavigationController the InitialViewController
2. make the FoodMenuController the root of UINavigationController
Now modify your FoodMenuController
#IBOutlet weak var bakeryButton: UIButton!
#IBOutlet weak var breakfastButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBarHidden = true //hide navigationBar in first ViewController
self.bakeryButton.addTarget(self, action: "bakeryButtonAction:", forControlEvents: .TouchUpInside)
self.breakfastButton.addTarget(self, action: "breakfastButtonAction:", forControlEvents: .TouchUpInside)
}
func bakeryButtonAction(sender: UIButton) {
performSegueWithIdentifier("showLunch", sender: self)
}
func breakfastButtonAction(sender: UIButton) {
performSegueWithIdentifier("showBreakfast", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let foodTableViewController: FoodTableViewController = segue.destinationViewController as! FoodTableViewController
if segue.identifier == "showBreakfast" {
foodTableViewController.contentMode = 1
} else if segue.identifier == "showLunch" {
foodTableViewController.contentMode = 2
}
}
Also you can make UINavigationBar visible in FoodTableViewController
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBarHidden = false
}
PS: It is always better not to add segue directly to a UIButton. Alternatively you can add it from the yellow button on top of your FoodMenuController and specify the segue to be fired in UIButtonAction using performSegueWithIdentifier
I can no where see you setting the datasource and delegate of the tableView, please cross check these are both setup.

Swift 2 Nil Delegate

So I want to create a IOS application that generates a group of students, adds them to a course and then shows students. I can show students in a list in a table view but now I want to let the user touch a student's name and be taken to a page with information about that student (name highest grade etc). The student class is flawless, the course works and the only problem I have is that I can't get a student from one view to the other.
Here's what I have so far:
//
// DataTableViewController.swift
// assignment8
//
import Foundation
import UIKit
class DataTableViewController: UITableViewController {
var delegate:StudentSelectionDelegate! = nil
var students = [Student]();
var course = Course();
// MARK: - UITableViewDataSource
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
func didSelectStudent(controller:UITableViewController, student:Student!) {
controller.navigationController?.popViewControllerAnimated(true)
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
self.course = courseStorage.getCourse();
self.students = course.getArrayOfStudentSortedByLastName();
return course.count;
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let row = indexPath.row
let currentStudent = students[row];
if (delegate != nil) {
delegate.didSelectStudent(self,student:currentStudent)
}
else {
print ("delegate is nil :(");
}
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("studentCell", forIndexPath: indexPath)
cell.textLabel?.text = students[indexPath.row].lastName + ", " +
students[indexPath.row].firstName;
return cell
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
print("ping");
if segue.identifier == "studentSegue" {
let nextScene = segue.destinationViewController as! studentViewController
// Pass the selected object to the new view controller.
if let indexPath = self.tableView.indexPathForSelectedRow {
let selectedStudent = students[indexPath.row]
print (selectedStudent.firstName);
nextScene.student = selectedStudent;
}
}
}
}
and
//
// DataViewController.swift
// assignment8
//
import UIKit
class DataViewController: UIViewController {
#IBOutlet weak var dataLabel: UILabel!
var dataObject: String = ""
let tableData = ["One","Two","Three"];
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.dataLabel!.text = dataObject
}
func tableView(tableView: UITableView!, numberOfRowsInSection section: Int)
-> Int {
return self.tableData.count;
}
}
and
//
// studentViewController.swift
// assignment8
//
import UIKit
protocol StudentSelectionDelegate {
func didSelectStudent(controller: UITableViewController, student:Student)
}
class studentViewController: UIViewController {
var delegate = StudentSelectionDelegate.self;
var name = String();
var student = Student();
#IBOutlet weak var StudentName: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func didSelectStudent(controller:UITableViewController, student:Student!) {
student.description;
print ("pong")
StudentName.text = student.firstName + " " + student.lastName;
controller.navigationController?.popViewControllerAnimated(true);
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// override func viewWillAppear(animated: Bool) {
// StudentName.text = name
// }
}
This is my storyboard so far.
So, any time I try clicking a student it will print the message that I've decided to use if the delegate is nil. So far I've tried looking at all the other answers on SO but none of them have fixed my issue.
To be able to send information from one view controller to another you should use segues. It seems like that's what you're doing according to the image. If you don't know how to use a segue, you can find a good answer here: Sending data with Segue with Swift
With segues you'll be able to set the delegate of the next view controller:
protocol MyDelegate {
func myFunction()
}
class FirstViewController: UIViewController, MyDelegate {
func myFunction() {
// do what the function does
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let secondVC = segue.destinationViewController as? SecondViewController {
secondVC.delegate = self
}
}
}
class SecondViewController: UIViewController {
var delegate: MyDelegate!
}
Before you segue to the second view controller (you're preparing for the segue), you set the delegate variable of SecondViewController to self, because FirstViewController conforms to MyDelegate protocol so it can be used there. Now, in SecondViewController you can use delegate.myFunction() and it will do whatever is written inside the FirstVC's function, because the FirstVC is SecondVC's delegate.

Swift - Segue on Dynamic TableCell

I'm learning swift, and i'm having problems trying to fire a segue when a TableViewCell is touched, which is supposed to pass an url to a second view that for the moment just a displays it in a label.
i Dynamically create ( i've seen people use Programmatically, which is probably the right word) every single Cell, so, in the storyboard i don't have any object to link to another view except the view itself... and that's what i did.
So i connected the first view controller to the second, and added the code to perform the segue.
i'm not sure if it's right, my knowledge comes from tutorials that didn't exactly explain what i wanted to do.
down there there's the code of the two views.
first view
import UIKit
protocol sendInfoDelegate{
func userDidEnterInfo( WhichInfo info : String)
}
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var tableData = []
#IBOutlet weak var redditListTableView: UITableView!
var selectedCellURL : String?
var delegate : sendInfoDelegate? = nil
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// change the link to change the json source
getRedditJSON("http://www.reddit.com/.json")
}
//
//Creates a connection via a task (networktask) then parses the json
//
func getRedditJSON(whichReddit : String){
let mySession = NSURLSession.sharedSession()
let url: NSURL = NSURL(string: whichReddit)
let networkTask = mySession.dataTaskWithURL(url, completionHandler : {data, response, error -> Void in
var err: NSError?
var theJSON = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as NSMutableDictionary
let results : NSArray = theJSON["data"]!["children"] as NSArray
dispatch_async(dispatch_get_main_queue(), {
self.tableData = results
self.redditListTableView.reloadData()
})
})
networkTask.resume()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//needs to be implemented
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tableData.count
}
//creates the whole table
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "MyTestCell")
let redditEntry : NSMutableDictionary = self.tableData[indexPath.row] as NSMutableDictionary
cell.textLabel?.text = redditEntry["data"]!["title"] as? String
cell.detailTextLabel?.text = redditEntry["data"]!["author"] as? String
return cell
}
// action to be taken when a cell is selected
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let redditEntry : NSMutableDictionary = self.tableData[indexPath.row] as NSMutableDictionary
self.selectedCellURL = redditEntry["data"]!["url"] as? String
self.performSegueWithIdentifier("passInfo" , sender: indexPath)
println(self.selectedCellURL!)
if delegate != nil {
let information:String = self.selectedCellURL!
println("ciao")
delegate?.userDidEnterInfo(WhichInfo: information)
self.navigationController?.popViewControllerAnimated(true)
}
second view
import UIKit
class WebPageController : UIViewController, sendInfoDelegate {
var infoFromSVC: String?
#IBOutlet weak var labelVC: UILabel!
func userDidEnterInfo(WhichInfo info: String) {
self.infoFromSVC = info
labelVC.text = infoFromSVC
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "passInfo"{
let firstVController : ViewController = segue.destinationViewController as ViewController
firstVController.delegate = self
}
}
}
Thanks.
To pass any data to second view controller you need to implement prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) method in your first view controller and here pass any data to your second view controller through segue.destinationViewController object.
for example
// this method must be in first view controller
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "passInfo" {
var secondViewController : SecondViewController = segue.destinationViewController as SecondViewController
var indexPath = self.tableview.indexPathForSelectedRow() //get index of data for selected row
secondViewController.data = self.dataArray.objectAtIndex(indexPath.row) // get data by index and pass it to second view controller
}
}
The code for getting data in second view controller
override func viewDidLoad() {
super.viewDidLoad()
self.label.text = self.data
}
The data variable must be defined as a property of your second view controller.

Resources