I am making an application that allows the user to keep order at usernames and passwords. Currently, I am having a problem when the user quit the app, the data or TableViewCell is not stored or showing up. I am using an array.
I assume because the data is not getting stored after. Is CoreData or UserDefaults a simple solution for this? I want to avoid Firebase.
Can someone explain or show, how to implement CoreData/UserDefaults into the code?
I have searched a lot but I simply find it hard to understand how to apply it in my code, especially with arrays and TableViewCell. Help is deeply appreciated.
Pictures are below.
Here is my code:
MainViewController:
class MainViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView:UITableView?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return informasjoner.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "InformasjonTableViewCell") as! InformasjonTableViewCell
let informasjon:Informasjon = informasjoner[indexPath.row];
if(informasjon.image != nil){
cell.imageViewInfo?.image = informasjon.image
} else {
cell.imageViewInfo?.image = UIImage(named: informasjon.imageName!)
}
cell.epostLabel?.text = informasjon.labelEpost
cell.passordLabel?.text = informasjon.labelPassord
cell.applikasjonLabel?.text = informasjon.labelApplikasjon
return cell
}
// EDIT / UPDATE CELL
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let informasjon = informasjoner[indexPath.row]
let deleteAction = UITableViewRowAction(style: .default, title: "Delete"){
(action, indexPath) in
self.deleteAction(informasjon: informasjon, indexPath: indexPath)
}
//call delete action
deleteAction.backgroundColor = .red;
return [deleteAction]
}
private func deleteAction(informasjon: Informasjon, indexPath: IndexPath){
let alert = UIAlertController(title: "Delete",
message: "Are u sure?",
preferredStyle: .alert)
let deleteAction = UIAlertAction(title: "Yes",
style: .default){ (action) in
informasjoner.remove(at: indexPath.row)
self.tableView?.deleteRows(at: [indexPath], with: .automatic)
}
let cancelAction = UIAlertAction(title: "No",
style: .default,
handler: nil);
alert.addAction(deleteAction);
alert.addAction(cancelAction);
present(alert, animated: true);
}
}
InformasjonTableViewCell:
import UIKit
class InformasjonTableViewCell: UITableViewCell {
#IBOutlet weak var imageViewInfo: UIImageView?
#IBOutlet weak var epostLabel: UILabel?
#IBOutlet weak var passordLabel: UILabel?
#IBOutlet weak var applikasjonLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
AddInfoVC:
import UIKit
class AddInfoVC: UIViewController, UITextFieldDelegate, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
#IBOutlet weak var txtEpost:UITextField?
#IBOutlet weak var ImageInfo:UIImageView?
#IBOutlet weak var txtPassord:UITextField?
#IBOutlet weak var txtApplikasjon: UITextField!
var newInfo = Informasjon()
#IBAction func btnSave(sender:UIButton){
print("Press Save!")
if(newInfo.image == nil || newInfo.labelEpost?.count == 0 || newInfo.labelPassord?.count == 0 || newInfo.labelApplikasjon?.count == 0){
let alertController = UIAlertController(title: "Alert", message: "Please set", preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default){
(action) in
}
alertController.addAction(okAction)
self.present(alertController, animated: true, completion: nil);
} else{
informasjoner.append(newInfo);
navigationController?.popViewController(animated: true)
let mainViewController = self.navigationController?.topViewController as? MainViewController
mainViewController?.tableView?.reloadData()
}
}
override func viewDidLoad() {
super.viewDidLoad()
//Tap to ImageView
let tapGestureToImageView = UITapGestureRecognizer(target: self, action: #selector(tapToImageView(sender:)))
tapGestureToImageView.numberOfTapsRequired = 1
ImageInfo?.isUserInteractionEnabled = true;
ImageInfo?.addGestureRecognizer(tapGestureToImageView);
self.txtEpost?.delegate = self;
self.txtPassord?.delegate = self;
self.txtApplikasjon?.delegate = self;
}
Models
Informasjon.swift:
import Foundation
import UIKit
class Informasjon {
var imageName: String?
var image: UIImage?
var labelEpost: String?
var labelPassord: String?
var labelApplikasjon: String?
convenience init(imageName:String, labelEpost:String, labelPassord:String,labelApplikasjon:String ) {
self.init()
self.imageName = imageName
self.labelEpost = labelEpost
self.labelPassord = labelPassord
self.labelApplikasjon = labelApplikasjon
}
}
Picture 1 -> How the Application looks from the Storyboard.
Picture 2 -> How the Application looks from Simulator.
UserDefaults usually is the simplest database to use. If you have only one array to store/retrieve then stick with it. If you have much more information go with something bigger like CoreData/Realm.
let array = []()
let defaults = UserDefaults.standard
defaults.set(array, forKey: "SavedStringArray")
Feel free to check out following answer and tutorial.
how to save and read array of array in NSUserdefaults in swift?
https://medium.com/#nimjea/userdefaults-in-swift-4-d1a278a0ec79
If it's still hard to understand I would recommend to check the Objective-C version of UserDefaults - NSUserDefaults. Even if you are not familiar with Objective-C syntax it's probably easier to understand.
Save string to the NSUserDefaults?
Now the question comes where to use these methods when working with UITableView.
Whenever you want to change or retrieve the data from the store (UserDefaults in our case).
For the instance you can change the stored array calling tableView(:cellForRowAt:) methods in the tableView(:didSelectRowAt:), or you can retrieve the data in tableView(_:cellForRowAt:)
For using Realm check the official website
https://realm.io/docs/swift/latest
You should use NSUserDefaults to store preferences only – just preferences for your app’s functionality and nothing else. Anything else like user data should be stored someplace else. Especially if said that is sensitive and you don’t want to risk your user’s data as the plist file is directly available in the apps directory.
One more disadvantage for using UserDefaults to store app data is all data will be stored as a property list file. The entire file is read in and written out as a whole, so if you use UserDefaults to store a large amount of data that only changes in parts, you will be wasting a lot of time doing unnecessary I/O.
Rather, CoreData/Realm is always a better option to persist app data.
To learn how to store in CoreData -
https://www.raywenderlich.com/173972/getting-started-with-core-data-tutorial-2
https://in.udacity.com/course/ios-persistence-and-core-data--ud325
https://medium.com/xcblog/core-data-with-swift-4-for-beginners-1fc067cca707
https://code.tutsplus.com/tutorials/core-data-and-swift-managed-objects-and-fetch-requests--cms-25068
To learn how to store in Realm -
https://realm.io/docs/tutorials/realmtasks/
https://www.appcoda.com/realm-database-swift/
https://www.raywenderlich.com/112544/realm-tutorial-getting-started
Related
So in my app I currently have a tableView with custom cells that have an imageView in them. Here is an image for reference to see what It look like and here is the code that defines the custom cell:
class GearComponentTableViewCell: UITableViewCell {
var delegate:Stepper?
var tableViewCellPosition: Int! = nil
// Image
#IBOutlet weak var itemImage: UIImageView!
// Name
#IBOutlet weak var itemName: UILabel!
// Weight
#IBOutlet weak var itemWeight1: UILabel!
#IBOutlet weak var itemWeight2: UILabel!
// Quanity
#IBOutlet weak var itemQuanity: UILabel!
#IBAction func stepperPressed (_ sender: UIStepper!) {
if (sender.value == 1) {
sender.value = 0
delegate?.stepperWasPressed(didIncrease: true, namePassed: itemName.text!, userindexPath: tableViewCellPosition)
} else if (sender.value == -1) {
sender.value = 0
delegate?.stepperWasPressed(didIncrease: false, namePassed: itemName.text!, userindexPath: tableViewCellPosition)
}
}
// Notes
#IBOutlet weak var itemNotes: UILabel!
}
These cells are tappable and when tapped they present an alert with options for adding an image to the cell you chose:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("User selected item: \(indexPath.row) and name is \(itemArray[indexPath.row].name)")
showPopUp(itemChosen: indexPath)
}
Here is the logic for the pop up/Alert:
func showPopUp(itemChosen: IndexPath) {
let alert = UIAlertController(title: "Add Photo To Item", message: .none, preferredStyle: .actionSheet)
// alert actions
let action1 = UIAlertAction(title: "Take Photo", style: .default) { action in
self.takePhoto()
}
let action2 = UIAlertAction(title: "Import Photo", style: .default) { action in
self.importPhoto(position: itemChosen)
}
let action3 = UIAlertAction(title: "Cancel", style: .destructive)
alert.addAction(action1)
alert.addAction(action2)
alert.addAction(action3)
present(alert, animated: true,completion: nil)
}
Ignore the takePhoto() function because it isn't being used yet, but right below that I am calling the importPhoto() function that takes in the position of the item you selected:
func importPhoto(position: IndexPath) {
print("User chose to import photo")
let picker = UIImagePickerController()
picker.allowsEditing = true
picker.delegate = self
present(picker, animated: true) {
print("cell tapped \(position)")
self.cellChosen = position
}
}
It then sets a global variable to the item you selected (I know this isn't a good practice vs. passing the actual indexPath) that I use to pass into the imagePickerControllerDelegate function:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
guard let image = info[.editedImage] as? UIImage else { return }
dismiss(animated: true)
if let cell = tableView(gearTableView, cellForRowAt: cellChosen) as? GearComponentTableViewCell {
cell.itemImage.image = image
self.updateUI()
print("item image set for the item at position: \(cellChosen.row)")
}else {
print("did not set item image")
}
}
This is where I am running into issues now. When that delegate function gets called it successfully prints the true-case in the if/else statement and the photo isn't actually being set and I'm not receiving any errors at the time it is set. The only error I receive is "Changing the translatesAutoresizingMaskIntoConstraints property of a UICollectionViewCell that is managed by a UICollectionView is not supported, and will result in incorrect self-sizing." when I tap the right bar-button item from the first photo in my post to add a new cell to the tableView. I require some info in the form of another Alert with textfields for some info about the item you are creating before the cell is actually created to I'm not sure if that error is actually relevant to what I am currently working on.
Any ideas as to how I can set the image of the imageView in my cell? I've already tried changing the Layout of my imageView from Inferred (constraints) to Auto-resizing mask and any new cells stopped appearing...
Here is a link to my repository/the viewController I was working in.
It's because you're not updating the image on the data source of the table view. You update when you dequeue the cell, but the you call updateUI that calls reloadData().
For instance, your method imagePickerController can work like this:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
guard let image = info[.editedImage] as? UIImage else { return }
dismiss(animated: true)
let item = itemArray[cellChosen.row]
let userSubmittedItem = GearItem(itemName: item.name,
itemImage: image,
itemWeight1: item.weight1,
itemWeight2: item.weight2,
itemQuantity: item.quantity,
itemNotes: item.notes)
itemArray[cellChosen.row] = userSubmittedItem
self.updateUI()
}
Change your implementation of method imagePickerController(_ picker:didFinishPickingMediaWithInfo:) to
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
guard let image = info[.editedImage] as? UIImage else { return }
picker.dismiss(animated: true)
itemArray[cellChosen.row].image = image
gearTableView.reloadRows(at: [cellChosen], with: .none)
}
When you need to update an image of a data source model. You update the model and call reload row.
About why your code doesn't work, set a breakpoint at func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell, and you will find your imageView's image is beening reset in above method.
I am building an iOS application for my year 12 project and it entails users being able to record information about a bill they have received and having the ability to take a photo of the bill and to save it. I have used core data for my users data to be saved to. I have currently been able to get the photo taken by the user to be able to be seen on a seperate screen when the user selects a bill. Where I am having trouble is that the YouTube video I used has only shown me how to display only 1 specific photo in position zero, as shown on line 39. I need help in getting a different image being displayed dependent on what bill the user selects. For example, if a user taps a water bill, on the viewing screen, they will see a water bill. Then if the user taps a gas bill, on the viewing screen, they will see the gas bill. Currently, what is happening is regardless of whether the user selects the gas or water bill, a water bill is displayed. I have tried to explain this the best I can, if there are any other concerns, please let me know.
Thank you for your assistance
import UIKit
import CoreData
class ViewControllerViewing: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// Getting keyboard to retract
}
func fetchImage() -> [Bravo] {
var fetchingImage = [Bravo]()
let fetchRequest = NSFetchRequest<Bravo>(entityName: "Bravo")
do {
fetchingImage = try context.fetch(fetchRequest)
} catch {
print("Error while fetching the image")
}
return fetchingImage
}
// Outlets
#IBOutlet weak var imgDisplay: UIImageView!
var selectedImage: String?
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
#IBOutlet weak var lblBill: UILabel!
// Actions
#IBAction func btnDisplay(_ sender: Any) {
let arr = DataBaseHelper.shareInstance.getAllImages()
// Got to get the numbered one change dependent on what bill is pressed
self.imgDisplay.image = UIImage(data: arr[0].photo!) // Only position zero photo displays
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
// Screen before photo screen
Class CarViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number bills
return bills.count
}
// Editing function
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Selected Person
let bravo = self.bills[indexPath.row]
// Create alert
let alert = UIAlertController(title: "Edit Bill", message: "Edit Provider", preferredStyle: .alert)
alert.addTextField()
// Edit text feild to edit provider
let txtProvider = alert.textFields![0]
// Configure button handler
let saveButton = UIAlertAction(title: "Save", style: .default) {
(action) in
// Edit provider property
bravo.provider = txtProvider.text
// Save new data
do {
try self.context.save()
}
catch {
}
// Refetch data
self.fetchBravo()
}
// Add button
alert.addAction(saveButton)
// Show alert
self.present(alert, animated: true, completion: nil)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//ensure the cell identifier has been labelled "cell"
// let bravob = self.bills[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
// Recieves information from core data
let display = self.bills[indexPath.row]
// Displays the provider in the title
cell.textLabel?.text = display.provider
// Displays the date in the subtitle
cell.detailTextLabel?.text = display.date
//How to add photo into tableview research
return cell
}
func fetchBravo() {
// Fetch data from core data to display in a tableview
do {
let request = Bravo.fetchRequest() as NSFetchRequest<Bravo>
// Set the filtering and sorting on the request This is the sorting method (For setting car filter, try to adjust here) Look at predecite https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Predicates/AdditionalChapters/Introduction.html
let pred = NSPredicate(format: "category CONTAINS '0'")
request.predicate = pred
self.bills = try context.fetch(request)
// Sort descripter
let sort = NSSortDescriptor(key: "provider", ascending: true)
request.sortDescriptors = [sort]
DispatchQueue.main.async {
self.tblCar.reloadData()
}
}
catch {
}
}
// Swipe to delete function https://www.youtube.com/watch?v=gWurhFqTsPU&list=RDCMUC2D6eRvCeMtcF5OGHf1-trw&start_radio=1
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
// Create swipe action
let action = UIContextualAction(style: .destructive, title: "Delete") { (action, view, completionHandler) in
// Which bill to removes
let BravoRemove = self.bills[indexPath.row]
// Remove Bill
self.context.delete(BravoRemove)
// Save updated delete
do {
try self.context.save()
}
catch{
}
// Refetch new data
self.fetchBravo()
}
// Return swipe action
return UISwipeActionsConfiguration(actions: [action])
}
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var bills:[Bravo] = []
override func viewDidLoad() {
super.viewDidLoad()
tblCar.delegate = self
tblCar.dataSource = self
tblCar.reloadData()
// Do any additional setup after loading the view.
}
// Outlets
#IBOutlet weak var txtDate: UITextField!
#IBOutlet weak var txtBill: UITextField!
#IBOutlet weak var tblCar: UITableView!
#IBOutlet weak var segCategory: UISegmentedControl!
// Actions
#IBAction func btnSearch(_ sender: Any) {
// Re-Fetch data for core data
self.fetchBravo()
print(bills)
tblCar.reloadData()
}
I have a viewController which holds three text fields. Each text field is connected to a label and uses NSUser Default so that when a user writes something in the textfield, the text is saved with a key - and showed in the label assigned to it. That works fine.
Two of the three text field are for the user to write "a question" and an "extra identifier". In case the user don't know what to write in these, in the top of the screen there are two buttons leading to two different tableViewControllers - both of which holds tableViews with inspiration/options that the user can choose. I implemented a segue from each, so that when the user taps a tableViewCell, the text in this cell is passed to the right textfield in viewController1 - and the user can now press save and save this text in the NSUser Default and let it be shown in the assigned label.
The problem is that these segues are ofcourse using ViewDidLoad to show the passed text from the inspiration tableViews. This means that when you enter the second tableViewController with inspiration after saving you chosen "question" from the first, then - because of the viewDidLoad method - all textfields and labels are cleared again and only the thing you just passed in there is shown... So:
Can I use another method than viewDidLoad to pass the data into ViewController1?
Or can I maybe add some code to the viewDidLoad method, so that it stops "resetting" all labels and textfields every time you enter the viewController?
Or do I need to go about it all a completely different way?
I hope it all makes sense - sorry about the long explanation! :D
Here's the relevant code from viewController1:
var chosenQuestion:String = ""
var chosenIdentifier:String = ""
override func viewDidLoad() {
super.viewDidLoad()
DiaryQuestionTextField.text = chosenQuestion
RandomIdentifierTextField.text = chosenIdentifier
}
#IBAction func WriteDiaryName() {
let title = "You have now chosen you diary's name. \n \n Do you wish to save this name and continue? \n \n You can always change all the details."
let alert = UIAlertController(title: title, message: nil, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
alert.addAction(UIAlertAction(title: "Save", style: .default, handler: { action in
self.defaults.setValue(self.DiaryNameTextField.text, forKey: "DiaryNameKey")
let NewDiaryName = self.defaults.string(forKey: "DiaryNameKey")
self.DiaryNameLabel.text = NewDiaryName
}))
present(alert, animated: true, completion: nil)
}
And there is ofcourse two similar methods for "WriteQuestion" and "WriteExtraIdentifier".
And here's the relevant code from one of the two tableViewControllers with "inspiration":
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.destination is UpdatingIdentifiers {
let vc = segue.destination as? UpdatingIdentifiers
let selectedRowIndex = self.tableView.indexPathForSelectedRow
let cell = self.tableView.cellForRow(at: selectedRowIndex!)
let label = cell?.viewWithTag(420) as! UILabel
vc?.chosenQuestion = label.text!
}
}
Thanks!
You can simply use a closure approach for this scenario.
Create a closure variable in ViewController2.
Set the value of this closure whenever you present ViewController2 from ViewController1.
Call the closure whenever you select a row in tableView of ViewController2 and pass the selected value in the closure.
Update the chosenQuestion whenever you get a new value from closure using which in turn will update the textField's value using the property observer.
Example Code:
class ViewController1: UIViewController
{
#IBOutlet weak var DiaryQuestionTextField: UITextField!
var chosenQuestion = "" {
didSet{
//Textfield will be updated everytime a new value is set in chosenQuestion variable
self.DiaryQuestionTextField.text = self.chosenQuestion
}
}
#IBAction func showInspirationVC(_ sender: UIButton)
{
let controller = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewController2") as! ViewController2
controller.handler = {[weak self](data) in
self?.chosenQuestion = data
}
self.present(controller, animated: true, completion: nil)
}
}
class ViewController2: UIViewController, UITableViewDelegate, UITableViewDataSource
{
var handler: ((String)->())?
var data = ["Question-1", "Question-2", "Question-3", "Question-4", "Question-5"]
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return self.data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = self.data[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
self.dismiss(animated: true) {
self.handler?(self.data[indexPath.row])
}
}
}
Storyboard:
Try implementing a simple use case first. And you don't need segues in this approach since you are presenting the controller programatically.
Let me know in case of any issue.
Simple, you just need to re-initialise "chosenQuestion" and "chosenIdentifier" with the values from UserDefaults in the viewDidLoad and you are good to go.
I'm making my first app, and it has a task manager, for this, I made a table view in which you can input "tasks" through an alert. How can I save the inputted "tasks"? I did some research, however, I was not able to find an answer.
The code is the following:
(Swift 2)
import UIKit
class ThirdViewController: UIViewController, UITableViewDataSource{
var items: [String] = []
#IBOutlet weak var listTableView: UITableView!
#IBAction func additem(sender: AnyObject) {
alert()
}
#IBAction func savebtn(sender: AnyObject) {
print (items)
}
override func viewDidLoad() {
super.viewDidLoad()
listTableView.dataSource = self
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("listitem") as! ItemTableViewCell
cell.itemLabel.text = items[indexPath.row]
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func alert() {
let alert = UIAlertController(title: "", message: "", preferredStyle: .Alert)
alert.addTextFieldWithConfigurationHandler{
(textfield) in
textfield.placeholder = "Enter your task"
}
let add = UIAlertAction(title: "Add ", style: .Default) {
(action) in
let textfield = alert.textFields![0]
self.items.append(textfield.text!)
self.listTableView.reloadData()
}
let cancel = UIAlertAction(title: "Cancel", style: .Cancel) {
(alert) in
print ("Hi")
}
alert.addAction(add)
alert.addAction(cancel)
presentViewController(alert, animated: true, completion: nil)
}
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
items.removeAtIndex(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
I'm not sure whether I'm understanding your question correctly, but I think it might help to read up on Apple's Tutorial and follow the "Persist Data" part.
(Link: https://developer.apple.com/library/content/referencelibrary/GettingStarted/DevelopiOSAppsSwift/PersistData.html#//apple_ref/doc/uid/TP40015214-CH14-SW1)
It is a step-by-step tutorial that comes with detailed explanations. Should be able to you a good idea of how to save data for a simple app. (If a larger database is needed, Core Data is something to read about.)
You can save any content local or in the cloud,
Local -> Core Data or Realm
Cloud -> Your custom API (obviously you are not ready for that) or Cloud API like (Firebase, Backendless)
Watch some tutorials for all of them, choose one for your purpose and vision about the app in the future.
Good luck.
The easiest way to Persist Data or store data even after your application is terminated or closed is use RMMapper,it work like nsuserdefaults but by using RMMapper we can store whole object..
//set
UserDefaults.standard.rm_setCustomObject(Obj1, forKey: "myObject")
//retrive
let obj = UserDefaults.standard.rm_customObject(forKey: "myObject")
On my main.Storyboard I have a TableViewController which is set to a custom class with TableViewController.swift.
The swift file has all the tableview functions defined and the #IBOutlet for the UITableView connected. The classes defined are UINavigationController,UITableViewDelegate. This viewController is called from a secondViewController via the prepareForSegue function.
I also created CustomCell.swift with class UITableViewCell and all #IBOutlet for the labels in my UITableViewCell which has been set to the customCell class.
I can't paste all my code but if you need to look at any specific code let me know and I will be happy to post that.
The Build succeeds and the app runs but the tableviewcells don't show up and none of the tableview functions are called. I see 2 flash animated screens - indicating that the tableviewcell might have 2 views - but can't figure out where I should be checking?
//Below is the segue function triggering the TableViewController
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
//print("In prepareswgue: ",segue, " ",sender)
if(segue.identifier == "resultSegue")
{
let nav = segue.destinationViewController as! UINavigationController
let svc = nav.topViewController as! TableViewController
svc.serialNo = self.TSSerialNoField.text
}
}
//Below is the custom TableVIewController class code
class TableViewController: UINavigationController,UITableViewDataSource, UITableViewDelegate
{
var serialNo:String!
var ashHardwareData: NSMutableArray!
#IBOutlet var ResultTableView: UITableView!
//#IBOutlet weak var LogCaseButton: UIButton!
#IBOutlet weak var TypeResultLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
self.ResultTableView?.allowsSelectionDuringEditing = true
self.ResultTableView?.delegate = self
ResultTableView?.dataSource = self
}
override func viewDidAppear(animated: Bool) {
self.getHardwareData(serialNo.uppercaseString)
}
/*override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
}*/
func getHardwareData(serialno:String)
{
ashHardwareData = NSMutableArray()
ashHardwareData = ModelManager.getInstance().getHardwareData(serialno)
ResultTableView?.reloadData()
}
//TableView Delegate Methods
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
print("In height func")
return 50
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(ashHardwareData.count)
return ashHardwareData.count
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView:UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:ResultsCell = tableView.dequeueReusableCellWithIdentifier("results", forIndexPath: indexPath) as! ResultsCell
let hardware:HardwareInfo = ashHardwareData.objectAtIndex(indexPath.row) as! HardwareInfo
let contract:ContractInfo = ashHardwareData.objectAtIndex(indexPath.row) as! ContractInfo
cell.SNOLabel.text = "Serial N0: \(hardware.SerialNo)"
cell.ContractIDLabel.text = "Contract ID: \(contract.ContractID)"
cell.OrgLabel.text = "Organisation: \(hardware.Organisation)"
cell.ModelLabel.text = "Model: \(hardware.Model)"
if(contract.DaystoExpiry > 0) {
cell.TypeLabel.text = "Contract Type: Active"
self.TypeResultLabel.hidden = false
self.TypeResultLabel.text = "To log a technical case for the Hardware please click on Log Technical Case button."
cell.LogCaseButton.hidden = false
cell.LogCaseButton.tag = indexPath.row
}
else {
cell.TypeLabel.text = "Contract Type: Expired"
cell.LogCaseButton.hidden = true
self.TypeResultLabel.hidden = false
self.TypeResultLabel.text = "Support Contract for the hardware expired. Please contact Sales team to renew the contract."
}
return cell
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
//here is the code for the ResultsCell custom UITableViewCell
import Foundation
import UIKit
class ResultsCell: UITableViewCell {
#IBOutlet weak var SNOLabel: UILabel!
#IBOutlet weak var LogCaseButton: UIButton!
#IBOutlet weak var ContractIDLabel: UILabel!
#IBOutlet weak var OrgLabel: UILabel!
#IBOutlet weak var TypeLabel: UILabel!
#IBOutlet weak var ModelLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
have you configured ResultsCell in story board or in view didload. check it once. if not try to add the following code into your view didload
[self.tableView registerNib:[UINib nibWithNibName:#"ResultsCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:#"ResultsCell"];
I have spent fair bit of my time trying to get this issue resolved and decided to go without the tableviewcell, instead I have created custom viewControllers and defined labels to display the results from the DB search (which is working OK) and used the perform segue function to pass values between viewcontrollers. So now my app works the way I want - maybe not ideal from a programming side but due to time constraints I had to get this working.
I have my app as a Tabbed Application with First and Second ViewControllers. The FirstViewController adds data to the SQLLite DB and the Second searches for the data and displays the results. for displaying the results I created a Custom viewController with labels for the data that I wanted displayed and passed all data from the DB results in SecondViewController functions to it and updated the labels with the data. As long as I get the results I wanted I am happy. I will re-visit this for improvement if I have to. Thanks to all who responded with solutions and suggestions. It has been a good learning experience :)