unabe to Reload data in tableview via userdefault - swift

I am a newbie and learning ios programming. I have made a custom table view cell. I am trying to append data in it via user defaults but its not storing the data, every time i go back the app refreshes making the table view blank screen.
// data sent from here
class ViewController: UIViewController {
#IBOutlet weak var textfield: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func submit(_ sender: AnyObject) {
performSegue(withIdentifier: "move", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destination = segue.destination as? secondviewcontroller
destination?.list.append(textfield.text!)
let defaults = UserDefaults.standard
defaults.set(destination?.list, forKey: "listarray")
UserDefaults.standard.synchronize()
}
}
// here i want to retreive
class secondviewcontroller: UIViewController {
#IBOutlet weak var tblView: UITableView!
var list = ["fahad","Ali","tahir"]
var abc = ""
override func viewDidLoad() {
super.viewDidLoad()
tblView.dataSource = self;
tblView.delegate = self;
let storedarray = UserDefaults.standard.object(forKey: "listarray")
print("hi\(storedarray)")
}
override func viewDidAppear(_ animated: Bool) {
tblView.register(TableViewCell.self, forCellReuseIdentifier: "TableViewCell")
tblView.reloadData()
}
#IBAction func backbtn(_ sender: AnyObject) {
dismiss(animated: true, completion: nil)
}
}
import UIKit
class secondviewcontroller: UIViewController {
#IBOutlet weak var tblView: UITableView!
var list = ["fahad","Ali","tahir"]
var abc = ""
override func viewDidLoad() {
super.viewDidLoad()
tblView.dataSource = self;
tblView.delegate = self;
}
override func viewDidAppear(_ animated: Bool) {
tblView.register(TableViewCell.self, forCellReuseIdentifier: "TableViewCell")
let storedarray = UserDefaults.standard.object(forKey: "listarray")
print("hi\(storedarray)")
tblView.reloadData()
}
#IBAction func backbtn(_ sender: AnyObject) {
dismiss(animated: true, completion: nil)
}
}
extension secondviewcontroller : UITableViewDataSource , UITableViewDelegate{
func numberOfSections(in tableView: UITableView) -> Int {
return 1;
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print (list)
return list.count;
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath) as! TableViewCell;
cell.textlabel.text = list[indexPath.row];
return cell;
}
}
// my custom table view
import UIKit
class TableViewCell: UITableViewCell {
#IBOutlet weak var textlabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
}
My UI storyboardgetting this error

you must subclass the UITableViewDelegate and UITableViewDataSource methods and implement the required methods for it to display the data in your custom view.
refer to this: http://clean-swift.com/refactoring-table-view-data-source-and-delegate-methods/
try adding the code for stored array into the viewwillappear method..

These lines recreates the same list with an new element without considering the saved items:
destination?.list.append(textfield.text!)
let defaults = UserDefaults.standard
defaults.set(destination?.list, forKey: "listarray")
UserDefaults.standard.synchronize()
You should instead:
let newEntry = textfield.text!
let defaults = UserDefaults.standard
if destination?.list.count == 3 { //3 because you hardcoded 3 items in next controller
destination?.list.append(newEntry)
} else {
let obj = defaults.object(forKey: "listarray") as? [String] ?? [String]()
destination?.list = obj.append(newEntry)
}
defaults.set(destination?.list, forKey: "listarray")
UserDefaults.standard.synchronize()

Related

Passing access from tableViewController to another view controller

I have spent a fair bit of time on this and still can't seem to figure out what I'm doing wrong.
I would like to run the reloadData() function in my own function on another view controller than the tableViewController but I get the error Type 'HomeViewController' has no member 'reloadData'.
HomeViewController:
import UIKit
import CoreData
class HomeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var index = ""
var item : [ListItem] = [] //listName
var listName = [""]
override func viewDidLoad() {
super.viewDidLoad()
self.homeListsTableView.delegate = self
self.homeListsTableView.dataSource = self
homeListsTableView.reloadData()
//List Names
//List Items - Within a list Name
let listItem1 = ListItem()
listItem1.name = "" //Update so names are updated via append the ListItem Array
listItem1.location = ""
item.append(listItem1)
let listItem2 = ListItem()
listItem2.name = ""
listItem2.location = ""
item.append(listItem2)
}
#IBOutlet weak var homeListsTableView: UITableView!
#IBAction func templatesButton(_ sender: Any) {
tabBarController?.selectedIndex = 2
}
override func viewWillAppear(_ animated: Bool) {
if let context = (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer.viewContext {
if let coreDataListItems = try? context.fetch(ListName.fetchRequest()) as? [ListName] {
print(coreDataListItems)
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return listName.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = listName[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// performSegue(withIdentifier: "goToItems", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as! ListNameViewController
vc.homeListsTableViewVC = homeListsTableView
}
}
ListName View Controller:
import UIKit
import CoreData
class ListNameViewController: UIViewController, UITableViewDelegate {
// var listName = [""]
let context = (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer.viewContext
var homeListsTableViewVC = HomeViewController.self
override func viewDidLoad() {
super.viewDidLoad()
}
#IBOutlet weak var listNameValue: UITextField!
#IBOutlet weak var locationOption: UITextField!
#IBOutlet weak var createButtonChange: UIButton!
#IBAction func createButton(_ sender: Any) {
let newList = ListName(context: context!)
newList.listName = listNameValue.text
// let location = locationOption.text!
//tabBarController?.selectedIndex = 0
//performSegue(withIdentifier: "home", sender: nil)
}
func saveList() {
do {
try context!.save()
} catch {
print("Error saving context \(error)")
}
homeListsTableViewVC.reloadData()
}
}
I'm trying to use the prepare for segue function to pass the homeListsTableView data to then use the reloadData() function. Could someone tell me what I'm doing wrong?
Thanks!
You can use delegate or closure to update your tableview.. here is pointers of how you can use closure in your classes to update view
class ListNameViewController: UIViewController, UITableViewDelegate {
var callback: (()->())?
//....
func saveList() {
do {
try context!.save()
} catch {
print("Error saving context \(error)")
}
callback?()
}
}
And while creating ListNameViewController
class HomeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as! ListNameViewController
vc.callback = { [weak self] in
self?.homeListsTableView.reloadData()
}
}
}

Blank labels after data passing in Swift 4?

I am currently trying to pass data from my table view controller to a second view controller but my labels and image are appearing as blank in the second view controller.
This is my main event view controller:
class EventsTableViewController: PFQueryTableViewController {
override func queryForTable() -> PFQuery<PFObject> {
let query = PFQuery(className: "Events")
//query.order(byAscending: "location")
query.order(byAscending: "date")
return query
}
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Upcoming Events"
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "MasterToDetail" {
let detailVC = segue.destination as! DetailViewController
detailVC.myEventCell = sender as? EventCell
}
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath, object: PFObject?) -> PFTableViewCell? {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! EventCell
cell.dateLabel.text = object?.object(forKey: "date") as? String
cell.locationLabel.text = object?.object(forKey: "location") as? String
cell.nameLabel.text = object?.object(forKey: "name") as? String
let imageFile = object?.object(forKey: "image") as? PFFile
cell.eventImage.image = UIImage(named: "download")
cell.eventImage.file = imageFile
cell.eventImage.loadInBackground()
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "MasterToDetail", sender: EventCell())
}
#IBAction func reloadTable(_ sender: Any) {
self.loadObjects()
}
#IBAction func onSignOutTapped(_ sender: Any) {
let firebaseAuth = Auth.auth()
do {
try firebaseAuth.signOut()
performSegue(withIdentifier: "signOutSegue", sender: nil)
} catch {
print (error)
}
}
}
and my second view controller:
class DetailViewController: UIViewController {
#IBOutlet weak var detailImageView: UIImageView!
#IBOutlet weak var detailNameLabel: UILabel!
#IBOutlet weak var detailDescriptionLabel: UILabel!
#IBOutlet weak var detailLocationLabel: UILabel!
var myEventCell: EventCell?
override func viewDidLoad() {
super.viewDidLoad()
setUI()
}
func setUI () {
detailNameLabel.text = myEventCell?.nameLabel.text
detailImageView.image = myEventCell?.eventImage.image
detailLocationLabel.text = myEventCell?.locationLabel.text
}
}
As you send an empty cell here
performSegue(withIdentifier: "MasterToDetail", sender: EventCell())
You need
let cell = tableView.cellForRow(at:indexPath)
performSegue(withIdentifier: "MasterToDetail", sender:cell)

UILabel throwing Thread1: EXC_BAD_ACCESS (code=1, address 0x…) in Swift 2

when I would like to make the data from the valise, table view controller (SelectedCity) ,app crash and error : Thread1: EXC_BAD_ACCESS (code=1, address 0x…)
error is in line labelcity!.text = tit varibale tit not problem ,I think the problem lies in the UIlabel (labelcity)
can you help me?
AircraftSearch
class AircraftSearch: UIViewController ,SendbackDelegate{
#IBOutlet weak var Mabda: UIButton!
#IBOutlet weak var maghsad: UIButton!
#IBOutlet weak var labelcity: UILabel!
var Airurl = NSURL()
var ScrOrDstArray = [MabdaAndMaghsad]()
var origin = [String]() // save mabda
var purpose = [String]() // save maghsad
var sendDataToTableview = [String]()
var tit = String()
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
navigationController?.setNavigationBarHidden(false, animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
GetPassCity()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func selectMabda(sender: AnyObject) {
sendDataToTableview = origin
performSegueWithIdentifier("SelectedCellSegue", sender: sender)
}
#IBAction func selectMaghsad(sender: AnyObject) {
sendDataToTableview = purpose
print(sendDataToTableview)
performSegueWithIdentifier("SelectedCellSegue", sender: sender)
}
func originAndpurpose() {
let dataCity = ScrOrDstArray
for i in dataCity{
if i.SrcOrDst == true{
origin.append(i.Name)
}else{
purpose.append(i.Name)
}
}
}
func sendNameToPreviousVC(SelectCity: String) {
print("\(tit) selected ") //return data
tit = SelectCity
labelcity!.text = tit
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "SelectedCellSegue" {
if let VC = segue.destinationViewController as? SelectedCity {
VC.toTake = sendDataToTableview
VC.delegate = self
}
}
}
}
SelectedCity view
import UIKit
protocol SendbackDelegate:class {
func sendNameToPreviousVC(City:String)
}
class SelectedCity: UITableViewController {
var toTake = [String]()
var selecteCity = String()
weak var delegate: SendbackDelegate? = nil
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
navigationController?.setNavigationBarHidden(false, animated: true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return toTake.count ?? 0
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("stcell", forIndexPath: indexPath) as? mAndMCell
let nameCity = toTake[indexPath.row]
print(nameCity)
cell!.nameCityLabel.text = nameCity
return cell!
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){
let indexPath = tableView.indexPathForSelectedRow!
let currentCell = tableView.cellForRowAtIndexPath(indexPath) as! mAndMCell!
selecteCity = currentCell.nameCityLabel!.text as String!
sendBackIdCity(selecteCity)
navigationController?.popViewControllerAnimated(true)
}
func sendBackIdCity(name: String){
self.delegate?.sendNameToPreviousVC(name)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "backCitySegue"{
var VCOne = segue.destinationViewController as? AircraftSearch
self.delegate = VCOne
}
}
}
Instead of
#IBOutlet weak var labelcity: UILabel? = UILabel()
try:
#IBOutlet weak var labelcity: UILabel!
Consider to remove weak keyword in the property declaration. Usage of this keyword prevents just created UILabel object from it's retaining, so the object deallocates immediately.
var labelcity: UILabel? = UILabel()
Or another option is to move object instantiation into viewDidLoad method:
var labelcity: UILabel!
...
override func viewDidLoad() {
super.viewDidLoad()
let label = UILabel()
//you code for subview adding into view controller's view
labelcity = label
GetPassCity()
}
I solved the problem:
override func viewDidLoad() {
super.viewDidLoad()
labelcity.text = tit
GetPassCity()
}
func sendNameToPreviousVC(SelectCity: String) {
tit = SelectCity
}

tableView.reloadData() causing a crash of the app

In the Swift app, I present an addition view to add an element to the CoreData database. If I call tableview.reloadData(), the app crashes on the + button on the main screen. If I omit the reload data then the add view is presented and the data is added to the CoreData file.
the main view, from configureCell down:
func configureCell(cell: TransectTableViewCell, indexPath:NSIndexPath) {
let transectEntry = fetchedResultController.objectAtIndexPath(indexPath) as! Transects
cell.transectNameLabel.text = transectEntry.transectName
cell.transectNameLabel.textColor = UIColor.blackColor()
cell.transectNameLabel.shadowColor = UIColor.whiteColor()
cell.transectNameLabel.shadowOffset = CGSizeMake(1, 1)
}
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
let countEntry = fetchedResultController.objectAtIndexPath(indexPath) as! Transects
coreDataStack.context.deleteObject(countEntry)
coreDataStack.saveContext()
}
}
func tableView(tableView: UITableView,
heightForRowAtIndexPath indexPath: NSIndexPath)
-> CGFloat {
return 50;
}
func didFinishViewController(viewController: AddTransectViewController, didSave: Bool) {
if didSave {
var error: NSError? = nil
let context = viewController.context
self.coreDataStack.saveContext()
}
dismissViewControllerAnimated(true, completion: {})
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "addTransectSegue" {
let newTransectViewController = segue.destinationViewController as! AddTransectViewController
let transectEntryEntity = NSEntityDescription.entityForName("Transects", inManagedObjectContext: coreDataStack.context)
let newTransectEntry = Transects(entity: transectEntryEntity!, insertIntoManagedObjectContext: coreDataStack.context)
newTransectViewController.transectNewEntry = newTransectEntry
newTransectViewController.context = newTransectEntry.managedObjectContext
newTransectViewController.delegate = self
}
if segue.identifier == "transectTasksSegue" {
let indexPath = tableView.indexPathForSelectedRow()!
let transectSelected = fetchedResultController.objectAtIndexPath(indexPath) as! Transects
let tasksViewController = segue.destinationViewController as! TransectTasksViewController
tasksViewController.coreDataStack = coreDataStack
tasksViewController.selectedTransect = transectSelected
}
}
func controllerDidChangeContent(controller:
NSFetchedResultsController) {
tableView.reloadData()
}
The addition view is:
import UIKit
import CoreData
import Foundation
protocol TransectDelegate {
func didFinishViewController(ViewController:AddTransectViewController, didSave:Bool)
}
class AddTransectViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var transectNameTextField: UITextField!
#IBOutlet weak var latitudeTextField: UITextField!
#IBOutlet weak var longitudeTextField: UITextField!
#IBOutlet weak var altitudeTextField: UITextField!
var transectNewEntry: Transects!
var context: NSManagedObjectContext!
var delegate:TransectDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
override func prefersStatusBarHidden() -> Bool {
return true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func updateTransectEntry() {
if let entry = transectNewEntry {
entry.transectName = transectNameTextField.text
entry.latitude = latitudeTextField.text
entry.longitude = longitudeTextField.text
entry.altitude = altitudeTextField.text
}
}
#IBAction func cancelButtonWasTapped(sender: AnyObject) {
delegate?.didFinishViewController(self, didSave: false)
}
#IBAction func saveButtonWasTapped(sender: AnyObject) {
updateTransectEntry()
delegate?.didFinishViewController(self, didSave: true)
}
}
I am missing something, but cannot see what. Ideas would be welcome.
The app hangs up on cell.transectNameLabel.text = transectEntry.transectName
with: Thread 1:EXC_BAD_ACCESS (code=1, address=0x0)
My real confusion is that this works perfectly:
import UIKit
import CoreData
class PlantSpeciesViewController: UIViewController, NSFetchedResultsControllerDelegate, PlantSpeciesDelegate, UITableViewDataSource, UITableViewDelegate {
#IBOutlet var tableView:UITableView!
var coreDataStack: CoreDataStack!
lazy var fetchedResultController:
NSFetchedResultsController = self.plantSpeciesFetchedResultsController()
var plantSpecies: PlantSpecies!
var selectedFamily: PlantFamily!
var context: NSManagedObjectContext!
var plantFamilyName: String!
override func viewDidLoad() {
super.viewDidLoad()
tableView.backgroundColor = UIColor.clearColor()
view.backgroundColor = UIColor(patternImage: UIImage (named: "Monitor backdrop.png")!)
self.title = plantFamilyName
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func plantSpeciesFetchedResultsController()
->NSFetchedResultsController {
fetchedResultController =
NSFetchedResultsController(
fetchRequest: plantSpeciesFetchRequest(),
managedObjectContext: coreDataStack.context,
sectionNameKeyPath: nil,
cacheName: nil)
fetchedResultController.delegate = self
var error: NSError? = nil
if (!fetchedResultController.performFetch(&error)){
println("Error: \(error?.localizedDescription)")
abort()
}
return fetchedResultController
}
func plantSpeciesFetchRequest() -> NSFetchRequest {
let fetchRequest = NSFetchRequest(entityName: "PlantSpecies")
fetchRequest.fetchBatchSize = 20
let predicate = NSPredicate(format: "familyName == %#", selectedFamily)
fetchRequest.predicate = predicate
let sortDescriptor = NSSortDescriptor(key: "plantSpecies", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
//var error: NSError?
return fetchRequest
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return fetchedResultController.sections!.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return fetchedResultController.sections![section].numberOfObjects
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("plantSpeciesCell", forIndexPath: indexPath) as! PlantSpeciesTableViewCell
cell.backgroundColor = UIColor.clearColor()
configureCell(cell, indexPath: indexPath)
return cell
}
func configureCell(cell: PlantSpeciesTableViewCell, indexPath:NSIndexPath) {
let plantEntry = fetchedResultController.objectAtIndexPath(indexPath) as! PlantSpecies
cell.speciesNameLabel.text = plantEntry.plantSpecies
cell.speciesNameLabel.textColor = UIColor.blackColor()
cell.speciesNameLabel.shadowColor = UIColor.whiteColor()
cell.speciesNameLabel.shadowOffset = CGSizeMake(1, 1)
cell.speciesImageView.image = UIImage (data: plantEntry.plantImage)
}
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
let countEntry = fetchedResultController.objectAtIndexPath(indexPath) as! PlantFamily
coreDataStack.context.deleteObject(countEntry)
coreDataStack.saveContext()
}
}
func tableView(tableView: UITableView,
heightForRowAtIndexPath indexPath: NSIndexPath)
-> CGFloat {
return 90;
}
func didFinishViewController(viewController: AddPlantSpeciesViewController, didSave: Bool) {
if didSave {
var error: NSError? = nil
let context = viewController.context
self.coreDataStack.saveContext()
}
dismissViewControllerAnimated(true, completion: {})
}
// MARK: - Navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "addSpeciesSegue" {
let newPlantViewController = segue.destinationViewController as! AddPlantSpeciesViewController
let plantEntryEntity = NSEntityDescription.entityForName("PlantSpecies", inManagedObjectContext: coreDataStack.context)
let newSpeciesEntry = PlantSpecies(entity: plantEntryEntity!, insertIntoManagedObjectContext: coreDataStack.context)
newPlantViewController.selectedFamily = selectedFamily
newPlantViewController.plantNameEntry = newSpeciesEntry
newPlantViewController.context = newSpeciesEntry.managedObjectContext
newPlantViewController.delegate = self
}
}
func controllerDidChangeContent(controller:
NSFetchedResultsController) {
tableView.reloadData()
}
}
coupled with:
import UIKit
import CoreData
import Foundation
protocol PlantSpeciesDelegate {
func didFinishViewController(ViewController:AddPlantSpeciesViewController, didSave:Bool)
}
class AddPlantSpeciesViewController: UIViewController, UITextFieldDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet weak var plantNameTextField: UITextField!
#IBOutlet weak var plantImageView: UIImageView!
#IBOutlet weak var imageSwitch: UISwitch!
#IBOutlet weak var imageFromFileButton: UIButton!
#IBOutlet weak var imageFromCameraButton: UIButton!
let imagePicker = UIImagePickerController()
var plantNameEntry: PlantSpecies!
var selectedFamily: PlantFamily!
var passedPlantFamily: String!
var newPlantName: String!
var newImageData: NSData!
var context: NSManagedObjectContext!
var delegate:PlantSpeciesDelegate?
override func viewDidLoad() {
super.viewDidLoad()
plantImageView.image = UIImage(named: "placeholder image.jpg")
imagePicker.delegate = self
}
override func prefersStatusBarHidden() -> Bool {
return true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func imageSourceSwitch(sender: AnyObject) {
if imageSwitch.on == true
{
self.imageFromFileButton.enabled = true
self.imageFromCameraButton.enabled = false
}
else
{
self.imageFromCameraButton.enabled = true
self.imageFromFileButton.enabled = false
}
}
#IBAction func imageFromFile(sender: AnyObject) {
imagePicker.sourceType = .PhotoLibrary
presentViewController(imagePicker, animated: true, completion: nil)
}
#IBAction func imageFromCamera(sender: AnyObject) {
imagePicker.sourceType = .Camera
presentViewController(imagePicker, animated: true, completion: nil)
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage!, editingInfo: [NSObject : AnyObject]!) {
self.plantImageView.image = image
dismissViewControllerAnimated(true, completion: nil)
}
#IBAction func getPlantName() {
newPlantName = plantNameTextField.text
plantNameTextField.resignFirstResponder()
}
func updateSpeciesEntry() {
if let entry = plantNameEntry {
entry.plantSpecies = newPlantName
entry.plantImage = UIImageJPEGRepresentation(plantImageView.image, 1.0)
entry.familyName = selectedFamily
}
}
#IBAction func cancelButtonWasTapped(sender: AnyObject) {
delegate?.didFinishViewController(self, didSave: false)
}
#IBAction func saveButtonWasTapped(sender: AnyObject) {
updateSpeciesEntry()
delegate?.didFinishViewController(self, didSave: true)
}
}
So, what is the difference?
This happen to me too, fixed it with:
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
instead of just tableView.reloadData()
, cause it seems that it was being called from wrong thread.
The reason for the crash is most likely that the table cannot load the data, for example a value does not exist and is being force unwrapped. The crash only happens, therefore, when you try to collect the data. Check through all the values to be sure.
I'd like to expand on GJZ answer & what brought me here. I have UITableViewCells that have a textfield. I attempted to get values the user entered from the field.
let companyNameCell: TextEntryCell = self.tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as! TextEntryCell
what I realized is that when I dynamically hide and showed rows by changing the cell heights using the tableview.reloadrows method, the app would crash if it attempted to read a cell the user could not see on their screen. that is because the cells I think were deallocated.

How to pass data to AddController to UITableViewController

I have a problem with my swift code. I have an UITableViewCotroller with a prototype cell, in this cell there are three labels. I created the file TimerCell.swift with the #IBOutlet for the labels:
import UIKit
class TimerCell: UITableViewCell {
#IBOutlet var circostanza: UILabel!
#IBOutlet var luogo: UILabel!
#IBOutlet var tempo: 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
}
}
and the file TimerModel.swift with this code:
import UIKit
class TimerModel: NSObject, NSCoding {
var circostanza :String!
var luogo : String!
var tempo : String!
init(circostanzaIn:String, luogoIn:String, tempoIn:String) {
circostanza = circostanzaIn
luogo = luogoIn
tempo = tempoIn
}
internal required init(coder aDecoder: NSCoder) {
self.circostanza = aDecoder.decodeObjectForKey("circostanza") as! String
self.luogo = aDecoder.decodeObjectForKey("luogo") as! String
self.tempo = aDecoder.decodeObjectForKey("tempo") as! String
}
func encodeWithCoder(encoder: NSCoder) {
encoder.encodeObject(self.circostanza, forKey: "circostanza")
encoder.encodeObject(self.luogo, forKey: "luogo")
encoder.encodeObject(self.tempo, forKey: "tempo")
}
}
Then I have a button + in the UITableViewCotroller to open an AddController with three text field to add some data. I want to save these data in the cell's labels. The code in the AddController is:
import UIKit
class AddTimerController: UIViewController, UITextFieldDelegate {
#IBOutlet var fieldCircostanza: UITextField!
#IBOutlet var fieldLuogo: UITextField!
#IBOutlet var fieldTempo: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
fieldCircostanza.delegate = self
fieldLuogo.delegate = self
//
var keyboardToolbar = UIToolbar(frame: CGRectMake(0, 0, self.view.bounds.size.width, 44))
keyboardToolbar.barStyle = UIBarStyle.BlackTranslucent
keyboardToolbar.backgroundColor = UIColor.redColor()
keyboardToolbar.tintColor = UIColor.whiteColor()
var flex = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FlexibleSpace, target: nil, action: nil)
var save = UIBarButtonItem(title: "Fatto", style: UIBarButtonItemStyle.Done, target: fieldTempo, action: "resignFirstResponder")
keyboardToolbar.setItems([flex, save], animated: false)
fieldTempo.inputAccessoryView = keyboardToolbar
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder() // chiudere la tastiera nei campi di testo
return true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func annulla(sender: UIButton) {
dismissViewControllerAnimated(true, completion: nil) // chiude una modal
}
#IBAction func salva(sender: UIButton) {
if fieldCircostanza.text.isEmpty &&
fieldLuogo.text.isEmpty &&
fieldTempo.text.isEmpty{
//alertView
return
}
var timer = TimerModel(circostanzaIn: fieldCircostanza.text,
luogoIn: fieldLuogo.text,
tempoIn: fieldTempo.text)
DataManager.sharedInstance.salvaArray()
DataManager.sharedInstance.detail.myCollection.reloadData()
dismissViewControllerAnimated(true, completion: nil)
}
}
Finally the UITableViewCotroller file:
import UIKit
class TimerViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
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 DataManager.sharedInstance.storage.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! TimerCell
//cell.circostanza.text = timer.circostanza
//cell.luogo.text = timer.luogo
//cell.tempo.text = timer.tempo
return cell
}
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 func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
DataManager.sharedInstance.storage.removeAtIndex(indexPath.row)
DataManager.sharedInstance.salvaArray()
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.
}
}
// MARK: - Navigazione
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "modifica" {
//let controller = (segue.destinationViewController as UINavigationController).topViewController as AddController
}
}
}
Can you help me with the code for saving data?
for your query in the comment, this will correct that:
In your TimerModel:
import UIKit
class TimerModel: NSObject, NSCoding {
var circostanza :String!
var luogo : String!
var tempo : String!
override init() { // add this method
super.init()
}
init(circostanzaIn:String, luogoIn:String, tempoIn:String) {
circostanza = circostanzaIn
luogo = luogoIn
tempo = tempoIn
}
internal required init(coder aDecoder: NSCoder) {
self.circostanza = aDecoder.decodeObjectForKey("circostanza") as! String
self.luogo = aDecoder.decodeObjectForKey("luogo") as! String
self.tempo = aDecoder.decodeObjectForKey("tempo") as! String
}
func encodeWithCoder(encoder: NSCoder) {
encoder.encodeObject(self.circostanza, forKey: "circostanza")
encoder.encodeObject(self.luogo, forKey: "luogo")
encoder.encodeObject(self.tempo, forKey: "tempo")
}
}
For accessing timer in tableViewController, do this:
import UIKit
class AddTimerController: UIViewController, UITextFieldDelegate {
#IBOutlet var fieldCircostanza: UITextField!
#IBOutlet var fieldLuogo: UITextField!
#IBOutlet var fieldTempo: UITextField!
var timer: TimerModel! // Made timer a class member
override func viewDidLoad() {
......
}
// your code ....
#IBAction func salva(sender: UIButton) {
if fieldCircostanza.text.isEmpty &&
fieldLuogo.text.isEmpty &&
fieldTempo.text.isEmpty{
//alertView
return
}
timer = TimerModel(circostanzaIn: fieldCircostanza.text, // intialize timer
luogoIn: fieldLuogo.text,
tempoIn: fieldTempo.text)
DataManager.sharedInstance.salvaArray()
DataManager.sharedInstance.detail.myCollection.reloadData()
dismissViewControllerAnimated(true, completion: nil)
}
}
and after that in tableViewController, access it like this:
class TimerViewController: UITableViewController {
var timer: TimerModel! // Change to this
override func viewDidLoad() {
super.viewDidLoad()
var controller : AddTimerController = AddTimerController()
timer = controller.timer
}
// your code .......
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! TimerCell
cell.circostanza.text = timer.circostanza
cell.luogo.text = timer.luogo
cell.tempo.text = timer.tempo
return cell
}
}