尝试保存到 NSUserdefaults 时出错
Error when trying to save to NSUserdefaults
我有一个 table,其工作方式如下。如果某行被 selected,对应于 selected 行的金额将添加到 var total
,并添加复选标记。如果是 deselected,金额将被减去,复选标记将设置为 none。
在尝试 save/retrieve 数据 to/from NSUserDefaults 之前,我的代码工作正常。
错误是:当我 select 第一行时,它减去金额而不是添加它;在一行 selected 之后,如果我点击后退按钮然后返回同一屏幕,则不会显示复选标记。
在此构造中,当我尝试从 NSUserDefaults indexPathForCellSelected
检索时,即使我在进行分配之前检查了 if indexPathForCellSelected != nil
,计算结果仍为 nil。
致命错误:在展开可选值时意外发现 nil
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)
}
}
}
设置数据后尝试同步NSUserDefaults。
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject("1", forKey: "SomeKey")
defaults.synchronize() // Sync the defaults to update the data
尝试在tableView(_:didSelectRowAtIndexPath:)
函数中放置一个表达式来添加复选标记,您应该也可以在其中根据其状态实现加减法。所以你可以这样做:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if let cell = tableView.cellForRowAtIndexPath(indexPath) {
if cell.accessoryType == .None {
cell.accessoryType = .Checkmark
total += extras[indexPath.row].amount
} else if cell.accessoryType == .Checkmark {
cell.accessoryType = .None
total -= extras[indexPath.row].amount
}
// Call function to save here
}
}
这是我问题的正确答案。如果有人有更好的方法,post 你在答案部分的版本。
class NinethViewController: UIViewController, UITableViewDelegate,UITableViewDataSource {
var total = 0
var totalKey = "totalNinethViewControllerKey"
let indexPathKey = "indexPathForCellSelectedNinethViewControllerKey"
var indexPathsForCellSelected: NSMutableSet = NSMutableSet()// {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
self.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 once
extras[indexPath.row].selected = true
tableView.cellForRowAtIndexPath(indexPath)?.accessoryType = .Checkmark
self.total += extras[indexPath.row].amount
indexPathsForCellSelected.addObject(indexPath)
print("selecting")
print(total)
} else {
// marks the cell as not selected
extras[indexPath.row].selected = false
tableView.cellForRowAtIndexPath(indexPath)?.accessoryType = .None
self.total -= extras[indexPath.row].amount
indexPathsForCellSelected.removeObject(indexPath)
print(total)
}
// save all info in NSUserDefaults
self.saveData()
}
func saveData() {
// save indexPathsForCellSelected in NSUserDefaults
let data = NSKeyedArchiver.archivedDataWithRootObject(indexPathsForCellSelected)
NSUserDefaults.standardUserDefaults().setObject(data, forKey: indexPathKey)
//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? NSMutableSet{
indexPathsForCellSelected = data1
}
}
// assign checkmark to row selected
for item in indexPathsForCellSelected {
if let indexPath = item as? NSIndexPath {
extras[indexPath.row].selected = true
self.tableView.cellForRowAtIndexPath(indexPath)?.accessoryType = .Checkmark
/* let tableV = self.tableView
let cell = tableV.cellForRowAtIndexPath(indexPath)
extras[indexPath.row].selected = true
cell?.accessoryType = .Checkmark */
}
}
// retrieve total from NSUserDefaults
if let totalRetrieved = NSUserDefaults.standardUserDefaults().objectForKey(totalKey) as? Int {
total = totalRetrieved
print(total)
}
}
}
我不知道你为什么要为之前选择的单元格保存所有 NSIndexPath
,而根据你之前的问题和这个问题,你只需要保存所选单元格的索引和当您转到另一个控制器时,您拥有的总数的价值。您可以通过以下方式进行:
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
}
}
希望对你有所帮助。
我有一个 table,其工作方式如下。如果某行被 selected,对应于 selected 行的金额将添加到 var total
,并添加复选标记。如果是 deselected,金额将被减去,复选标记将设置为 none。
在尝试 save/retrieve 数据 to/from NSUserDefaults 之前,我的代码工作正常。
错误是:当我 select 第一行时,它减去金额而不是添加它;在一行 selected 之后,如果我点击后退按钮然后返回同一屏幕,则不会显示复选标记。
在此构造中,当我尝试从 NSUserDefaults indexPathForCellSelected
检索时,即使我在进行分配之前检查了 if indexPathForCellSelected != nil
,计算结果仍为 nil。
致命错误:在展开可选值时意外发现 nil
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)
}
}
}
设置数据后尝试同步NSUserDefaults。
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject("1", forKey: "SomeKey")
defaults.synchronize() // Sync the defaults to update the data
尝试在tableView(_:didSelectRowAtIndexPath:)
函数中放置一个表达式来添加复选标记,您应该也可以在其中根据其状态实现加减法。所以你可以这样做:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if let cell = tableView.cellForRowAtIndexPath(indexPath) {
if cell.accessoryType == .None {
cell.accessoryType = .Checkmark
total += extras[indexPath.row].amount
} else if cell.accessoryType == .Checkmark {
cell.accessoryType = .None
total -= extras[indexPath.row].amount
}
// Call function to save here
}
}
这是我问题的正确答案。如果有人有更好的方法,post 你在答案部分的版本。
class NinethViewController: UIViewController, UITableViewDelegate,UITableViewDataSource {
var total = 0
var totalKey = "totalNinethViewControllerKey"
let indexPathKey = "indexPathForCellSelectedNinethViewControllerKey"
var indexPathsForCellSelected: NSMutableSet = NSMutableSet()// {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
self.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 once
extras[indexPath.row].selected = true
tableView.cellForRowAtIndexPath(indexPath)?.accessoryType = .Checkmark
self.total += extras[indexPath.row].amount
indexPathsForCellSelected.addObject(indexPath)
print("selecting")
print(total)
} else {
// marks the cell as not selected
extras[indexPath.row].selected = false
tableView.cellForRowAtIndexPath(indexPath)?.accessoryType = .None
self.total -= extras[indexPath.row].amount
indexPathsForCellSelected.removeObject(indexPath)
print(total)
}
// save all info in NSUserDefaults
self.saveData()
}
func saveData() {
// save indexPathsForCellSelected in NSUserDefaults
let data = NSKeyedArchiver.archivedDataWithRootObject(indexPathsForCellSelected)
NSUserDefaults.standardUserDefaults().setObject(data, forKey: indexPathKey)
//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? NSMutableSet{
indexPathsForCellSelected = data1
}
}
// assign checkmark to row selected
for item in indexPathsForCellSelected {
if let indexPath = item as? NSIndexPath {
extras[indexPath.row].selected = true
self.tableView.cellForRowAtIndexPath(indexPath)?.accessoryType = .Checkmark
/* let tableV = self.tableView
let cell = tableV.cellForRowAtIndexPath(indexPath)
extras[indexPath.row].selected = true
cell?.accessoryType = .Checkmark */
}
}
// retrieve total from NSUserDefaults
if let totalRetrieved = NSUserDefaults.standardUserDefaults().objectForKey(totalKey) as? Int {
total = totalRetrieved
print(total)
}
}
}
我不知道你为什么要为之前选择的单元格保存所有 NSIndexPath
,而根据你之前的问题和这个问题,你只需要保存所选单元格的索引和当您转到另一个控制器时,您拥有的总数的价值。您可以通过以下方式进行:
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
}
}
希望对你有所帮助。