将NSUserDefaults与结构类型数组一起使用

时间:2016-10-15 18:41:38

标签: ios arrays swift struct nsuserdefaults

我有问题要弄清楚如何保存我的字符串" RiskEntry"与NSUserDefaults。我已经完成了其他一些帖子,但不知怎的,我仍然无法解决这个问题。

让我解释下面的代码现在做了什么:我在以下代码片段中从我的类CustomCell获取了一些数据。在这里,我首先检查"标识符"使用新数组值更新哪个数组"后果"。

一切正常,更新的数组存储在riskEntry中。 但是,我现在无法解决如何使用NSUserDefaults存储它。当我尝试使用例如riskItemDefaults.set(riskEntry, forKey: "riskItem")我收到异常错误。

知道我在这里做错了吗?

SWIFT3 (我删除了与此问题无关的所有代码)

class: RiskPlan: UIViewController, UITableViewDelegate, UITableViewDataSource, CustomCellUpdaterDelegate {

 var riskEntry = [RiskEntry]()
 var riskItemDefaults = UserDefaults.standard

// ------ start of delegate function (receiving from CustomCell) ---------
      func transferData(consequencesTranferred: String, identifier: String) {
        if let index = riskEntry.index(where: {$0.title as String == identifier}) {
            riskEntry[index].consequences = consequencesTranferred
    } else {
        print ("nothing")
    }

// save with NSUserDefaults
        riskItemDefaults.set(riskEntry, forKey: "riskItem")
  }
}

这是我的结构:

public struct RiskEntry {
    let title: String
    var consequences: String
}

我的自定义单元格

// ---------------- delegate to transfer entered data to VC -----------------
protocol CustomCellUpdaterDelegate {
    func transferData(consequencesTranferred: String, identifier: String)
}

// ---------------- start of class CellCustomized -----------------
class CustomCell: UITableViewCell, UIPickerViewDataSource, UIPickerViewDelegate, UITextViewDelegate {

    var delegate: CustomCellUpdaterDelegate?

    // text fields, text views and picker views
    @IBOutlet weak var riskTitle: UITextView!
    @IBOutlet weak var consequences: UITextView!

    // ---------------- listener for text view to save input in string when editing is finished -----------------
    func textViewDidEndEditing(_ textView: UITextView) {
        if textView.tag == 1 {
            textConsequences = consequences.text
            nameIdentifier = riskTitle.text
            delegate?.transferData(consequencesTranferred: self.textConsequences, identifier: nameIdentifier)
        } else {
            print ("nothing")
        }
    }
}

4 个答案:

答案 0 :(得分:1)

问题是您无法在NSUserDefaults中保存自定义数组。为此,您应该将它们更改为NSData,然后将其保存在NSUserDefaults中 这是我在我的项目中使用的代码,它采用swift 2语法,我认为将其转换为swift 3并不困难

let data = NSKeyedArchiver.archivedDataWithRootObject(yourObject);
NSUserDefaults.standardUserDefaults().setObject(data, forKey: "yourKey")
NSUserDefaults.standardUserDefaults().synchronize()

和获取部分使用此组合

if let data = NSUserDefaults.standardUserDefaults().objectForKey("yourKey") as? NSData {
    let myItem = NSKeyedUnarchiver.unarchiveObjectWithData(data) as? yourType
}

希望这会有所帮助

答案 1 :(得分:1)

保存UserDefaults中的对象有非常具体的限制:

  

set(_:forKey:) reference:

     

value参数只能是属性列表对象:NSData,NSString,NSNumber,NSDate,NSArray或NSDictionary。对于NSArray和NSDictionary对象,它们的内容必须是属性列表对象。

您需要使用NSCoding序列化模型,或者使用JSON作为替代方案,以UserDefaults映射到支持的值。

答案 2 :(得分:0)

UserDefaults支持的Swift结构的最接近类型可能是NSDictionary。在保存数据之前,您可以将struct元素复制到Objective C NSDictionary对象中。

答案 3 :(得分:0)

我能够基于@ahruss(How to save an array of custom struct to NSUserDefault with swift?)编写解决方案。但是,我为swift 3修改了它,它还显示了如何在UITableView中实现此解决方案。我希望它可以在将来帮助某人:

  1. 将扩展名从下方添加到您的结构中(将其调整为您自己的变量)

  2. 保存所需的数组项,如下所示:

    let encoded = riskEntry.map {$ 0.encode()} riskItemDefaults.set(encoded,forKey:“后果”) riskItemDefaults.synchronize()

  3. 像这样加载你的项目

    让dataArray = riskItemDefaults.object(forKey:“后果”)为! [NSData的] let savedFoo = dataArray.map {RiskEntry(data:$ 0)! }

  4. 如果您想在单元格中显示已保存的数组项,请按以下方式进行操作:

    cell.consequences.text = savedFoo [indexPath.row] .consequences as String

  5. 以下是针对 Swift3

    修改的完整代码

    结构

    // ---------------- structure for table row content -----------------
    struct RiskEntry {
        let title: String
        var consequences: String
    }
    

    扩展

      extension RiskEntry {
            init?(data: NSData) {
                if let coding = NSKeyedUnarchiver.unarchiveObject(with: data as Data) as? Encoding {
                    title = coding.title as String
                    consequences = (coding.consequences as String?)!
                } else {
                    return nil
                }
            }
    
            func encode() -> NSData {
                return NSKeyedArchiver.archivedData(withRootObject: Encoding(self)) as NSData
            }
    
            private class Encoding: NSObject, NSCoding {
                let title : NSString
                let consequences : NSString?
    
    
                init(_ RiskEntry: RiskEntry) {
                    title = RiskEntry.title as NSString
                    consequences = RiskEntry.consequences as NSString?
                }
    
                public required init?(coder aDecoder: NSCoder) {
                    if let title = aDecoder.decodeObject(forKey: "title") as? NSString {
                        self.title = title
                    } else {
                        return nil
                    }
                    consequences = aDecoder.decodeObject(forKey: "consequences") as? NSString
                }
    
                public func encode(with aCoder: NSCoder) {
                    aCoder.encode(title, forKey: "title")
                    aCoder.encode(consequences, forKey: "consequences")
                }
    
            }
        }