尝试保存到NSUserdefaults时出现错误

3
我有一张表格,其工作方式如下。如果选择了某一行,则会将与所选行相应的金额添加到var total中,并添加一个复选标记。如果取消选择,则将减去该金额,并将复选标记设置为无。 在尝试将数据保存/检索到NSUserDefaults之前,我的代码正常运行。 错误是:当我选择第一行时,它会减去金额而不是添加;选择行后,如果我点击返回按钮然后再回到同一屏幕,则不显示复选标记。 在这个结构中,当我尝试从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)
      }
   }
}

看到我的回答了,抱歉回复晚了。最近一直很忙。 - Victor Sigler
4个回答

1
尝试在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
        }
    }

加减功能已经实现并按预期工作。当我使用NSUserDefaults添加/检索数据时出现了上述错误。你能否在我的当前NSUserDefaults实现中添加一些内容或告诉我出了什么问题? - bibscy
当您设置数据时,请尝试将其从NSKeyedArchiver...更改为NSUserDefaults.standardUserDefaults().objectForKey(indexPathForCellSelected ),看看是否有效。还要在if语句中设置断点,然后逐步执行以确保它进入其中。 - user5104686
它不起作用。您能否尝试在Xcode中运行我的代码? - bibscy
indexPathForCellSelected 是 NSIndexPath 类型的非属性列表对象,因此与 NSUserDefaults.standardUserDefaults().objectForKey 不兼容。 - bibscy

1
我不知道你为什么要保存之前选择的所有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
    }
}

我希望这可以帮助你。

我没有考虑使用indexValue构造NSIndexPath实例。感谢您的帮助。 - bibscy

0

这是我问题的正确答案。如果有更好的方法,请在答案部分发布您的版本。

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)
    }
}
}

-3
尝试在设置数据后同步NSUserDefaults。
let defaults = NSUserDefaults.standardUserDefaults()

defaults.setObject("1", forKey: "SomeKey")
defaults.synchronize() // Sync the defaults to update the data

1
defaults.synchronize() 的目的是立即将用户默认设置写入磁盘。iOS 只会在适当的时刻执行此操作。 - felipenbrito
1
"synchronize()已被弃用。这不是我的代码无法工作的原因。" - bibscy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接