【问题标题】:NSUserDefaults - Array is empty in second View ControllerNSUserDefaults - 第二个视图控制器中的数组为空
【发布时间】:2016-10-17 10:33:32
【问题描述】:

我有使用 NSUserDefaults 保存在“[RiskEntry]”类型数组中的数据。

让我解释一下我在这里保存的内容以及加载和保存过程在我的代码中的样子(我取出了不相关的代码,我正在使用 Swift3):

Main_ViewController

首先:在 Main_ViewController 类中,我从弹出窗口中获取了一个文本,并将其保存在一个数组中,其中 NSUserDefaults 在 riskEntry 中,键为“newTry”(这只是一个临时名称;-))-> 'func riskText( riskTextTransferred: String)'。

Plan_ViewController

第二个:当使用 segue 到达 Plan_ViewController 上的表格时,表格中会填充以“newTry”键(cell.riskTitle.text = savedTitles[indexPath.row].title as String)保存在 riskEntry 中的文本。到目前为止,这一切都很好。

CustomCellPlan_ViewController

第三:当用户在单元格的文本字段(consequences.text)中输入值时,为 riskEntry 更新值后果。所以一开始,数组有一个空字符串来表示后果。现在,条目已更新为文本。

if let index = savedTitle.index(where: {$0.title.contains(identifier)}) {
            savedTitle[index].consequences = consequencesTranferred

然后用键“newTry”保存新字符串。这也可以正常工作。

Main_ViewController

第四:现在我关闭 Plan_ViewController 以返回 Main_ViewController。当我现在尝试使用键“newTry”在 viewWillAppear() 中加载我的数组时,它是空的。

我做错了什么?如何在 Main_ViewController 中成功加载 Plan_ViewController 中的更新数据?

Main_ViewController

class Main_ViewController: UIViewController, UIPopoverPresentationControllerDelegate, UIGestureRecognizerDelegate {
    var riskItemDefaults = UserDefaults.standard
    var riskEntry = [RiskEntry]()

    override func viewWillAppear(_ animated: Bool) {

        if let dataArrayTitle = riskItemDefaults.object(forKey: "newTry") as? [NSData] {  
            var savedTitle = dataArrayTitle.map { RiskEntry(data: $0)! }
            riskItemDefaults.synchronize()

            // CHECKPOINT: all arrays are printed empty...
            print ("savedTitle: ")
            print (savedTitle)
            print ("dataArrayTitle: ")
            print (dataArrayTitle)
        } else {
           print ("nothing")
        }
    }


    // ---------------- delegate function that creates the array items -----------------
    func riskText(riskTextTransferred: String) {

        // CHECKPOINT: Array is saved correctly
        riskEntry = [RiskEntry(title: riskTextTransferred, consequences: "")]
        let encoded = riskEntry.map {$0.encode() }
        riskItemDefaults.set(encoded, forKey: "newTry")
        riskItemDefaults.synchronize()
    }
}

Plan_ViewController

class Plan_VC: UIViewController, UITableViewDelegate, UITableViewDataSource, CustomCellUpdaterDelegate  {
    @IBOutlet weak var tableView: UITableView!
    var riskEntry = [RiskEntry]()
    var riskItemDefaults = UserDefaults.standard

    // ---------------- table settings -----------------
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        // load saved data and count number of entries for numberOfSections
        let dataArrayTitles = riskItemDefaults.object(forKey: "newTry") as! [NSData]
        let savedTitles = dataArrayTitles.map { RiskEntry(data: $0)! }

        return savedTitles.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = self.tableView!.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CellCustomized
        cell.delegate = self

            // load saved data and insert title in cell label
            let dataArrayTitles = riskItemDefaults.object(forKey: "newTry") as! [NSData]
            let savedTitles = dataArrayTitles.map { RiskEntry(data: $0)! }
            cell.riskTitle.text = savedTitles[indexPath.row].title as String

        return cell
    }

    // ---------------- delegate function from CustomCell -----------------
    func transferData(consequencesTranferred: String, identifier: String) {

        /// load entry
        let dataArrayTitle = riskItemDefaults.object(forKey: "newTry") as! [NSData]
        var savedTitle = dataArrayTitle.map { RiskEntry(data: $0)! }
        riskItemDefaults.synchronize()

        // CHECKPOINT: The entry is loaded correctly
        print("loaded: ")
        print(savedTitle)

        /// filter entry for title contains identifier and update the empty string for consequences
        if let index = savedTitle.index(where: {$0.title.contains(identifier)}) {
            savedTitle[index].consequences = consequencesTranferred
            let encoded = riskEntry.map { $0.encode() }
            riskItemDefaults.set(encoded, forKey: "newTry")
            riskItemDefaults.synchronize()

            // CHECKPOINT: The entry is updated correctly
            print("After updating for consequences: ")
            print(savedTitle)
        }else {
            print ("No title with value identifier to be filtered")
        }

    }
}

自定义单元格

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

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

    var myColorsClass = myColors()
    var myStylesClass = myStyles()

    var delegate: CustomCellUpdaterDelegate?

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

    var nameIdentifier = String()
    var textConsequences = String()


    override func awakeFromNib() {
        super.awakeFromNib()

        // initiate textView delegate
        consequences.delegate = self    

    } // nib end


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

【问题讨论】:

  • 基本上NSUserDefaults 是保存临时数据的错误位置,尤其是在视图控制器之间传递的数据。
  • 我的数据不应该暂时保存。重新启动应用程序后数据应该仍然可用...
  • 我可能误解了你的问题,但是你只是想将数据从Plan_VC传递到Main_VC,然后保存这些数据,对吗?如果是这种情况,您应该使用委托,因为 NSUserDefaults 有时间延迟来保存数据。
  • 你说“解雇 Plan_ViewController”。这是怎么做到的?
  • 在我的导航中有一个按钮:'func backButtonAction(_ sender:UIButton!) {dismiss(animated: true, completion: nil)}'

标签: ios arrays swift nsuserdefaults


【解决方案1】:

因为NSUserDefaults有一个小的时间延迟,在你关闭Plan_ViewController的时候,newTry还没有保存(我认为它需要一些时间写入磁盘),但是@ `Main_ViewController 中的 987654324@ 方法会立即被调用。因此,由于时间延迟,数组是空的。

为了避免这种情况,您无法消除时间延迟,您可以做的是使用委托将数据传回您的第一个 VC。

因此,您应该直接从委托调用中执行此操作,而不是从 NSUserDefaults 检索数组。

【讨论】:

  • 使用synchronize时应该不会有延迟...假设它成功了。
  • @PhillipMills 是的,有一个不明显的延迟,可能是 2 毫秒。您可以首先通过在其他地方打印它来确保您的数组已正确保存(可能仍然在viewWillAppear 中打印,但这次您可以退出您的应用程序并重新启动,看看它是否打印了正确的数据)。如果属实,那么这个微小的延迟正在影响您的应用。
  • 如果 synchronize 实际上是异步的(即返回时可能不会写入数据),根据其文档,这将是一个错误。
  • 感谢您的帮助。我放弃了在代码中使用 UserDefaults 的想法,但决定改用 CodeData。现在效果好多了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多