I have a table which works in the following way. If a row is selected, an amount corresponding to the selected row is added to var total, a checkmark is added. If it is deselected, the amount will be subtracted, the checkmark will be set to none.
My code worked properly before trying to save/retrieve the data to/from NSUserDefaults.
The errors are: when I select the first row, it subtracts the amount instead of adding it; after a row is selected, if I tap on the back button and then come back to same screen the checkmark is not displayed.
In this construct when I try to retrieve from NSUserDefaults indexPathForCellSelected evaluates to nil even though I check if indexPathForCellSelected != nil before making the assignment.
fatal error: unexpectedly found nil while unwrapping an Optional value
if indexPathForCellSelected != nil { self.tableView.cellForRowAtIndexPath(indexPathForCellSelected!)?.
accessoryType = .Checkmark
}
class NinethViewController: UIViewController, UITableViewDelegate,UITableViewDataSource {
var total = 0
var indexRowRetrieved:Int?
var rowSelected:Int?
var indexRowSelectedKey = "indexRowKey"
var amountKey = "amountKey"
var totalKey = "totalKey"
let indexPathKey = "indexPathForCellSelected"
var indexPathForCellSelected: NSIndexPath?
struct Item {
var name:String // name of the row
var selected:Bool // whether is selected or not
var amount: Int // value of the item
}
var extras = [
Item(name:"Inside Cabinets",selected: false, amount: 5),
Item(name:"Inside Fridge",selected: false, amount: 5),
Item(name:"Inside Oven",selected: false, amount: 5),
Item(name:"Laundry wash & dry",selected: false, amount: 10),
Item(name:"Interior Windows", selected: false, amount: 5)
]
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
// loadData from NSUserDefaults
loadData()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return extras.count
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
// configure the cell
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath)
-> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell")
cell?.textLabel?.text = extras[indexPath.row].name
return cell!
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// if the cell has not been selected before
if !extras[indexPath.row].selected {
// marks the cell as selected
extras[indexPath.row].selected = true
tableView.cellForRowAtIndexPath(indexPath)?.accessoryType = .Checkmark
self.total += extras[indexPath.row].amount
indexPathForCellSelected = indexPath
rowSelected = indexPath.row
print(total)
// save all info in NSUserDefaults
saveData()
} else {
// marks the cell as not selected
extras[indexPath.row].selected = false
tableView.cellForRowAtIndexPath(indexPath)?.accessoryType = .None
self.total -= extras[indexPath.row].amount
print(total)
}
}
func saveData(){
// save indexPathForCellSelected in NSUserDefaults
if indexPathForCellSelected != nil {
let data = NSKeyedArchiver.archivedDataWithRootObject(indexPathForCellSelected!)
NSUserDefaults.standardUserDefaults().setObject(data, forKey: indexPathKey)
// save indexRowSelected
NSUserDefaults.standardUserDefaults().setInteger(rowSelected!, forKey: indexRowSelectedKey)
// save amount for corresponding row in NSUserDefaults
NSUserDefaults.standardUserDefaults().setObject(extras[rowSelected!].amount, forKey: amountKey)
//save total in NSUserDefaults
NSUserDefaults.standardUserDefaults().setObject(total, forKey: totalKey)
}
}
func loadData() {
//retrieve indexPathForCellSelected from NSUserDefaults
if let retrievedIndexPath = NSUserDefaults.standardUserDefaults().dataForKey(indexPathKey) {
if let data1 = NSKeyedUnarchiver.unarchiveObjectWithData(retrievedIndexPath) as? NSIndexPath{
indexPathForCellSelected = data1
}
}
// retrieve the index for row selected to select the state of the row: true or false
if let retrievedIndexRow = NSUserDefaults.standardUserDefaults().integerForKey(indexRowSelectedKey) as? Int {
indexRowRetrieved = retrievedIndexRow
extras[retrievedIndexRow].selected = true
}
// retrieve amount for corresponding row in NSUserDefaults
if let itemAmount = NSUserDefaults.standardUserDefaults().objectForKey(amountKey) as? Int {
extras[indexRowRetrieved!].amount = itemAmount
// assign checkmark to row selected
if indexPathForCellSelected != nil {
self.tableView.cellForRowAtIndexPath(indexPathForCellSelected!)?.accessoryType = .Checkmark
}
}
// retrieve total from NSUserDefaults
if let totalRetrieved = NSUserDefaults.standardUserDefaults().objectForKey(totalKey) as? Int {
total = totalRetrieved
print(total)
}
}
}
I don't know why you are saving all the NSIndexPath's for the cells selected previously when according your previous question and this one, you need to save only the index of the cell selected and the value of the total you had when you go to another controller. You can do it in the following way:
var frequency = [
Item(name:"Every week",selected: false, amount: 30),
Item(name:"Every 2 weeks",selected: false, amount: 30),
Item(name:"Every 4 weeks",selected: false, amount: 30),
Item(name:"Once",selected: false, amount: 40),
Item(name:"End of tenancy cleaning", selected: false, amount: 44)
]
var indexPathForCellSelected: NSIndexPath?
var total = 0
let indexPathKey = "indexPathForCellSelectedNinethViewControllerKey"
let totalKey = "totalNinethViewControllerKey"
override func viewDidLoad() {
super.viewDidLoad()
loadData()
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell.textLabel?.text = frequency[indexPath.row].name
if let _ = indexPathForCellSelected where indexPath == indexPathForCellSelected {
cell.accessoryType = .Checkmark
}
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if !frequency[indexPath.row].selected {
// this avoid set initial value for the first time
if let index = indexPathForCellSelected {
// clear the previous cell
frequency[index.row].selected = false
tableView.cellForRowAtIndexPath(index)?.accessoryType = .None
self.total -= frequency[index.row].amount
}
// mark the new one
frequency[indexPath.row].selected = true
tableView.cellForRowAtIndexPath(indexPath)?.accessoryType = .Checkmark
indexPathForCellSelected = indexPath
self.total += frequency[indexPath.row].amount
saveData()
}
}
private func saveData() {
// save the index for the cell selected in NSUserDefaults
NSUserDefaults.standardUserDefaults().setInteger(indexPathForCellSelected!.row, forKey: indexPathKey)
// save total in NSUserDefaults
NSUserDefaults.standardUserDefaults().setInteger(total, forKey: totalKey)
}
private func loadData() {
// get the values from NSUserDefaults if exist
if let indexValue = NSUserDefaults.standardUserDefaults().valueForKey(indexPathKey), totalValue = NSUserDefaults.standardUserDefaults().valueForKey(totalKey) {
indexPathForCellSelected = NSIndexPath(forRow: indexValue as! Int, inSection: 0)
total = totalValue as! Int
}
}
I hope this help you.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With