用户默认不保存,不检索数据

Posted

技术标签:

【中文标题】用户默认不保存,不检索数据【英文标题】:User defaults not saving, not retrieving data 【发布时间】:2020-09-12 23:25:11 【问题描述】:

我在我的应用程序的其他页面上成功使用了用户默认设置,它的工作方式与您期望的一样。在这个特定的控制器中,我从一个带有参数的结构中调用了一个方法,并将其分配为一个常量。据我所知,由于某种原因,此配置不会保存或从用户默认值中检索数据。我确信有一种方法,但我不知道正确的方法。一些指导在这里会有很大帮助。

所以,我正在尝试将文本字段保存为用户默认值,并在应用程序重新加载后调用文本字段数据和方法,以便用户恢复所有旧数据。现在什么也没有发生,我什至无法打印以排除故障,因为我真的不知道如何打印保存的状态。菜鸟在这里。

到目前为止,我已经尝试将保存点移动到各个地方,在按钮按下方法调用之前,调用之后,也尝试将用户默认值插入到 struct 方法中,都不行!我也尝试了各种检索方法,因为确实加载了,但到目前为止还没有。

这是我的代码当前所在的位置,非操作用户默认保存:

override func viewDidLoad() 
    area.delegate = self
    volume.delegate = self
    height.delegate = self
    
    // Check to see if there is a saved state, if there is then use those numbers and run method for even gauge
    let savedArea = StateManager.retrieveClearCalcValue(key: StateManager.areaKey) as? Double
    let savedVolume = StateManager.retrieveClearCalcValue(key: StateManager.volumeKey) as? Double
    let savedHeight = StateManager.retrieveClearCalcValue(key: StateManager.lengthKey) as? Double
    
    // If there is data in saved states, set text fields to saved data and call calculate
    if (savedArea != nil) && (savedVolume != nil) && (savedHeight != nil) 
        
        area.text = String(savedArea!)
        volume.text = String(savedVolume!)
        height.text = String(savedHeight!)
        
        let result = zoneCalc.clearCalc(area: Double(area.text!), volume: Double(volume.text!), height: Double(height!))
        
        areaBorder.text = String("\(result.0) mm")
        areaBorderLabel.text = "Cut result:"
    

按钮:

@IBAction func calcButtonPress(_ sender: Any) 
    
    // Resigns keyboard once button pressed
    self.view.endEditing(true)

        // State save attempt
        StateManager.saveClearZone(area: Double(area.text!), volume: Double(volume.text!), height: Double(height!))
    
        let result = clearCalc.clearZoneCalc(area: Double(area.text!) ?? 1.0, volume: Double(volume.text!) ?? 1.0, height: Double(height!))
        
        areaBorder.text = String("\(result.0) mm")
        areaBorderLabel.text = "Cut result:"
    

编辑(添加保存结构):

struct StateManager 

static var unitAreaKey = "UnitArea"
static var unitVolumeKey = "UnitVolume"
static var unitHeightKey = "UnitHeight"

// Saving user data
static func saveClearCalcState(area: Any, volume: Any, height: Any) 
    // Call reference to user defaults
    let defaults = UserDefaults.standard
    
    // Save state data
    defaults.set(area, forKey: unitAreaKey)
    defaults.set(volume, forKey: unitVolumeKey)
    defaults.set(height, forKey: unitHeightKey)


// Retrieve user data
static func retrieveClearCalcValue(key: String) -> Any? 
    //Call reference to user defaults
    let defaults = UserDefaults.standard
    
    return defaults.value(forKey: key)


【问题讨论】:

代码与UserDefaults 完全无关。我们怎么知道zoneCalcStateManagerstuff 是什么? 已修复... zoneCalc 只是一个计算器,可以处理从文本字段中获取的一些数字... calcButtonPress内部调用了StateManager.saveClearZone方法,但是您提供的代码中没有这样的方法,请您添加它吗? 解决了这个问题,这不是问题。这是我今天早上在漫长的夜班后发帖时的拼写错误。我必须更改我的代码以发布,因为它包含敏感的知识产权......简单地说:上面的代码可以很好地在视图控制器的函数调用中保存用户状态,但是在这个配置中我尝试在结构之前和之前保存方法调用我遇到了麻烦...我应该保存整个结构方法调用吗?可以这样做吗? ...我为这些难题道歉,仍然习惯如何在这里发帖。 让它在 viewDidLoad 中打印保存的值,看起来只有三个打印值之一,其他打印为零...我已经三次检查了我的代码是否良好。这是怎么回事?帮助表示赞赏:) 【参考方案1】:

我最好的猜测是,从 String 到 Double 的转换有时会在这一行失败(假设您打算写 saveClearCalcState 而不是 saveClearZone):

StateManager.saveClearZone(area: Double(area.text!), volume: Double(volume.text!), height: Double(height!))

Double 的字符串初始化程序是失败的,这意味着如果从字符串到数字的转换失败,它可以返回nil。由于尾随空格或非严格格式等各种问题,它可能会失败。

以下是一些解决方法的建议。

使用严格类型的函数参数并避免Any

目前,您的保存方法接受Any,但如果您知道只想在用户正确输入所有值时保存数据,请改用Double

static func saveClearCalcState(area: Double, volume: Double, height: Double)

如果您将参数类型更改为Double,您的代码将不再编译,因为像Double(area.text!) 这样的初始化程序会返回Double?,您必须先解开可选参数以检查值是否有效,即让用户知道他们的输入是否无法处理的好机会。

如果您的意图只是保存用户输入的任何内容以保留启动之间的状态,您可以选择String? 而不是Any。无论您选择什么而不是 Any,都会更清楚地声明该方法的用法。

使用NumberFormatter将字符串转换为数字

NumberFormatter 类为此任务提供了更多的灵活性。例如,您可以转换用户在其当前语言环境中键入的数字。例如,如果您的用户所在的国家/地区使用逗号作为小数分隔符,NumberFormatter 将允许他们使用他们喜欢的数字输入方式,而 Double 初始化程序始终需要句号作为小数分隔符。

使用可选链而不是强制展开

您的代码包含大量强制展开(感叹号运算符)。 请记住,尝试强制解开 nil 会使您的应用程序崩溃。 在某些情况下可能没问题,但仅仅因为文本字段中没有任何文本而使应用程序崩溃并不好。

首选optional chaining 强制解包。

优雅的 nil 处理示例

@IBAction func calcButtonPress(_ sender: Any) 
    
    // Resigns keyboard once button pressed
    self.view.endEditing(true)
    
    guard let areaText = area.text, !areaText.isEmpty else 
        // TODO: alert the user that area can't be empty
        return
    
    
    let numberFormatter = NumberFormatter()
    numberFormatter.numberStyle = .decimal
    numberFormatter.locale = Locale.current
    
    guard let areaValue = numberFormatter.number(from: areaText)?.doubleValue else 
        // TODO: alert the user that the text they entered can't be recognized as a numeric value
        return
    
    
    //...
    //... do the same checks for volume and height
    //...

    // State save attempt
    StateManager.saveClearZone(area: areaValue, volume: volumeValue, height: heightValue)

    let result = clearCalc.clearZoneCalc(area: areaValue, volume: volumeValue, height: heightValue)

    areaBorder.text = String("\(result.0) mm")
    areaBorderLabel.text = "Cut result:"    

【讨论】:

感谢您的详细帖子,我确实从数字格式化程序中学到了一些东西,我应用了它。我还通过合并处理选项。我还是有问题!密钥和价值保存的超简单性正在以某种方式被搞砸。所以到目前为止,我尝试了许多新的东西,比如将数字格式化为字符串,这就是现在保存的所有内容,保存双精度数对所有键返回 nil。保存字符串也很古怪,它将完美保存键 1 的值,但 2 和 3 总是不同的,有时我输入的倒数第二个值......没有意义。我什至尝试使用 .syncronize() - 不。 @AppMonster,我编辑了我的答案,包括一个在这种情况下我将如何处理 nil 的示例。如果您做了类似的事情但仍然遇到问题,请附上不起作用的实际代码,我很乐意提供帮助。 用所有代码更改了我下面的帖子。仍然没有工作,仍然为此绞尽脑汁。任何帮助都非常感谢!【参考方案2】:

@Vadim,首先再次感谢您的详细帖子。又学到了很多东西,应用了你的可选处理,从现在开始我将使用这些方法。非常简洁!

关于用户默认设置的问题......仍然有它们。这让我发疯。我尝试了很多不同的东西,我几乎无法一一列举,但我会在这里尝试:

我有:

重写了 3 次 State manager 结构并更改了许多不同的内容并尝试了各种不同的数据类型处理 将代码中的保存状态点多次移动到许多不同的点 下载了一个 cocopod “Defaults”,它最终遇到了与 UserDefaults 完全相同的问题 我已经用许多不同的配置改变了我的观点,加载方法至少 10 次 目前尝试了各种可选的 nil 处理

出了什么问题:

无论采用何种代码配置,我都会得到同样奇怪的数据检索,因为如果不是 nil,我会在重新启动时将值写入文本字段,我可以看到值(也打印它们)。并且始终第一个文本字段是正确的,但第二个和第三个是不正确的。有时第二个和第三个会显示我输入的最后一个数据,但不是最近的重启数据。就像它落后于一次重启一样。示例:

尝试一: 字段 1:12345 字段 2:1234 字段 3:123 重启--

加载: 显示- 字段 1:12345 字段 2: 字段 3: 新数据输入尝试: 字段 1:54321 字段 2:5432 字段 3:543 重启--

显示- 字段 1:54321 字段 2:1234 字段 3:123

WTF?

另外我必须声明,这个确切的状态对于其他 6 个视图控制器非常有效,并且使用完全相同的结构方法进行保存和检索。唯一的区别是在这个 VC 中我为计算器创建了一个类,而其他的计算器很小,所以它在 VC 中声明并调用为没有参数的函数......

无论如何,这里是我为 VC 编写的完整代码,未经修改,如果您愿意提供帮助,我将非常感激,并且在我有知识的情况下会在未来推动这一青睐:

//  Copyright © 2020 Roberto B. All rights reserved.
//

import UIKit

class CutCalculatorViewController: UIViewController 

// Length cut user inputs
@IBOutlet weak var overallLength: UITextField!
@IBOutlet weak var unitLength: UITextField!
@IBOutlet weak var headJointSize: UITextField!
@IBOutlet weak var metricImperialLengthSelector: UISegmentedControl!
// Length cut labels
@IBOutlet weak var buttonSelectorLabel: UILabel!
@IBOutlet weak var courseOneCut: UILabel!
@IBOutlet weak var courseOneCutLabel: UILabel!
@IBOutlet weak var courseTwoCut: UILabel!
@IBOutlet weak var courseTwoCutLabel: UILabel!


// Height cut user inputs
@IBOutlet weak var overallHeight: UITextField!
@IBOutlet weak var unitHeight: UITextField!
@IBOutlet weak var bedJointSize: UITextField!
@IBOutlet weak var metricImperialHeightSelector: UISegmentedControl!
// Height cut label
@IBOutlet weak var heightCutResult: UILabel!

override func viewDidLoad() 
    
    if StateManager.retrieveCutCalcValue(key: StateManager.overallLengthKey) != nil && StateManager.retrieveCutCalcValue(key: StateManager.unitLengthKey) != nil && StateManager.retrieveCutCalcValue(key: StateManager.headJointSizeKey) != nil && StateManager.retrieveCutCalcValue(key: StateManager.unitLengthSelector) != nil && StateManager.retrieveCutCalcValue(key: StateManager.unitLengthSelector) as? Int == 0 
        
        let savedOverallLength = StateManager.retrieveCutCalcValue(key: StateManager.overallLengthKey) as? Double
        let savedUnitLength = StateManager.retrieveCutCalcValue(key: StateManager.unitLengthKey) as? Double
        let savedHeadJoint = StateManager.retrieveCutCalcValue(key: StateManager.headJointSizeKey) as? Double
        metricImperialLengthSelector.selectedSegmentIndex = 0
        
        print(savedOverallLength!)
        print(savedUnitLength!)
        print(savedHeadJoint!)
        print(metricImperialLengthSelector.selectedSegmentIndex)
        
        
        overallLength.text = String(savedOverallLength!)
        unitLength.text = String(savedUnitLength!)
        headJointSize.text = String(savedHeadJoint!)
        
        let result = cutCalc.metricRunningBondCalc(overallLength: savedOverallLength!, unitLength: savedUnitLength!, headJointSize: savedHeadJoint!)
        
        courseOneCut.text = String("\(result.0) mm")
        courseTwoCut.text = "N/A"
        courseTwoCut.textColor = UIColor.clear
        courseTwoCutLabel.textColor = UIColor.clear
        buttonSelectorLabel.text = "Stack bond cut result"
        courseOneCutLabel.text = "Cut result:"
     else if StateManager.retrieveCutCalcValue(key: StateManager.overallLengthKey) != nil && StateManager.retrieveCutCalcValue(key: StateManager.unitLengthKey) != nil && StateManager.retrieveCutCalcValue(key: StateManager.headJointSizeKey) != nil && StateManager.retrieveCutCalcValue(key: StateManager.unitLengthSelector) != nil && StateManager.retrieveCutCalcValue(key: StateManager.unitLengthSelector) as? Int == 1 
        
        let savedOverallLength = StateManager.retrieveCutCalcValue(key: StateManager.overallLengthKey)
        let savedUnitLength = StateManager.retrieveCutCalcValue(key: StateManager.unitLengthKey)
        let savedHeadJoint = StateManager.retrieveCutCalcValue(key: StateManager.headJointSizeKey)
        metricImperialLengthSelector.selectedSegmentIndex = 1
        
        print(savedOverallLength!)
        print(savedUnitLength!)
        print(savedHeadJoint!)
        print(metricImperialLengthSelector.selectedSegmentIndex)
        
        
        overallLength.text = savedOverallLength as? String
        unitLength.text = savedUnitLength as? String
        headJointSize.text = savedHeadJoint as? String
        
        let result = cutCalc.imperialRunningBondCalc(overallLength: savedOverallLength as! Double, unitLength: savedUnitLength as! Double, headJointSize: savedHeadJoint as! Double)
        
        courseOneCut.text = String("\(result.0) inches")
        courseTwoCut.text = "N/A"
        courseTwoCut.textColor = UIColor.clear
        courseTwoCutLabel.textColor = UIColor.clear
        buttonSelectorLabel.text = "Stack bond cut result"
        courseOneCutLabel.text = "Cut result:"
        
    
    
    super.viewDidLoad()


// Initialize cut calculator structure
let cutCalc = CutCalculator()

// Initialize metric imperial segments
var unitLengthMeasurement: Bool = true
var unitHeightMeasurement: Bool = true

// Length cut buttons
// Stack bond- DONE
@IBAction func stackBondLength(_ sender: Any) 
    
    // Resigns keyboard once button pressed
    self.view.endEditing(true)
    
    // Activate methods for cut calculation metric/imperial & label changes
    if unitLengthMeasurement == true 
        
        guard let overallLength = overallLength.text, !overallLength.isEmpty else 
            let alert = UIAlertController(title: "Empty field!", message: "All text fields must contain data.", preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "Got it!", style: .cancel, handler: nil))
            self.present(alert, animated: true)
            return
        
        
        guard let unitLength = unitLength.text, !unitLength.isEmpty else 
            let alert = UIAlertController(title: "Empty field!", message: "All text fields must contain data.", preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "Got it!", style: .cancel, handler: nil))
            self.present(alert, animated: true)
            return
        
        
        guard let headJoint = headJointSize.text, !headJoint.isEmpty else 
            let alert = UIAlertController(title: "Empty field!", message: "All text fields must contain data.", preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "Got it!", style: .cancel, handler: nil))
            self.present(alert, animated: true)
            return
        
        
        let numberFormatter = NumberFormatter()
        numberFormatter.numberStyle = .decimal
        numberFormatter.locale = Locale.current
        
        guard let overallLengthValue = numberFormatter.number(from: overallLength)?.doubleValue else 
            // TODO: alert the user that the text they entered can't be recognized as a numeric value
            return
        
        
        guard let unitLengthValue = numberFormatter.number(from: unitLength)?.doubleValue else 
            // TODO: alert the user that the text they entered can't be recognized as a numeric value
            return
        
        
        guard let headJointValue = numberFormatter.number(from: headJoint)?.doubleValue else 
            // TODO: alert the user that the text they entered can't be recognized as a numeric value
            return
        
        let unitSelectorValue = metricImperialLengthSelector.selectedSegmentIndex
        
        StateManager.saveLengthCutCalcState(overallLength: overallLengthValue, unitLength: unitLengthValue, headJointSize: headJointValue, measurementLengthSelector: unitSelectorValue)
        
        let result = cutCalc.metricRunningBondCalc(overallLength: overallLengthValue, unitLength: unitLengthValue, headJointSize: headJointValue)
        
        courseOneCut.text = "\(result.0) mm"
        courseTwoCut.text = "N/A"
        courseTwoCut.textColor = UIColor.clear
        courseTwoCutLabel.textColor = UIColor.clear
        buttonSelectorLabel.text = "Stack bond cut result"
        courseOneCutLabel.text = "Cut result:"
        
        // Popup alert
        if result.0 < 36.0 
            // First instantiate the UIAlertController
            let alert = UIAlertController(title: "Small cut!", message: "Depending on conditions you may be able to open or squeeze head joints to eliminate this small cut.", preferredStyle: .alert)
            
            // Add action buttons to it and attach handler functions if you want to
            alert.addAction(UIAlertAction(title: "Got it!", style: .cancel, handler: nil))
            
            // Show the alert by presenting it
            self.present(alert, animated: true)
        
        
     else 
        
        guard let overallLength = overallLength.text, !overallLength.isEmpty else 
            let alert = UIAlertController(title: "Empty field!", message: "All text fields must contain data.", preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "Got it!", style: .cancel, handler: nil))
            self.present(alert, animated: true)
            return
        
        
        guard let unitLength = unitLength.text, !unitLength.isEmpty else 
            let alert = UIAlertController(title: "Empty field!", message: "All text fields must contain data.", preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "Got it!", style: .cancel, handler: nil))
            self.present(alert, animated: true)
            return
        
        
        guard let headJoint = headJointSize.text, !headJoint.isEmpty else 
            let alert = UIAlertController(title: "Empty field!", message: "All text fields must contain data.", preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "Got it!", style: .cancel, handler: nil))
            self.present(alert, animated: true)
            return
        
        
        let numberFormatter = NumberFormatter()
        numberFormatter.numberStyle = .decimal
        numberFormatter.locale = Locale.current
        
        guard let overallLengthValue = numberFormatter.number(from: overallLength)?.doubleValue else 
            // TODO: alert the user that the text they entered can't be recognized as a numeric value
            return
        
        
        guard let unitLengthValue = numberFormatter.number(from: unitLength)?.doubleValue else 
            // TODO: alert the user that the text they entered can't be recognized as a numeric value
            return
        
        
        guard let headJointValue = numberFormatter.number(from: headJoint)?.doubleValue else 
            // TODO: alert the user that the text they entered can't be recognized as a numeric value
            return
        
        let unitSelectorValue = metricImperialLengthSelector.selectedSegmentIndex
        
        StateManager.saveLengthCutCalcState(overallLength: overallLengthValue, unitLength: unitLengthValue, headJointSize: headJointValue, measurementLengthSelector: unitSelectorValue)
        
        let result = cutCalc.imperialRunningBondCalc(overallLength: overallLengthValue, unitLength: unitLengthValue, headJointSize: headJointValue)
        
        courseOneCut.text = "\(result.0) inches"
        courseTwoCut.text = "N/A"
        courseTwoCut.textColor = UIColor.clear
        courseTwoCutLabel.textColor = UIColor.clear
        buttonSelectorLabel.text = "Stack bond cut result"
        courseOneCutLabel.text = "Cut result:"
        
        // Popup alert
        if result.2 < 2.5 
            // First instantiate the UIAlertController
            let alert = UIAlertController(title: "Small cut!", message: "Depending on conditions you may be able to open or squeeze head joints to eliminate this small cut.", preferredStyle: .alert)
            
            // Add action buttons to it and attach handler functions if you want to
            alert.addAction(UIAlertAction(title: "Got it!", style: .cancel, handler: nil))
            
            // Show the alert by presenting it
            self.present(alert, animated: true)
        
    


// Running bond- DONE
@IBAction func runningBondLength(_ sender: Any) 
    // Resigns keyboard once button pressed
    self.view.endEditing(true)
    
    // Activate methods for cut calculation metric/imperial & label changes
    if unitLengthMeasurement == true 
        let result = cutCalc.metricRunningBondCalc(overallLength: Double(overallLength.text!) ?? 1.0, unitLength: Double(unitLength.text!) ?? 1.0, headJointSize: Double(headJointSize.text!) ?? 1.0)
        courseOneCut.text = String("\(result.0) mm")
        courseTwoCut.text = String("\(result.1) mm")
        buttonSelectorLabel.text = "Running bond cut results"
        courseTwoCut.textColor = #colorLiteral(red: 0.7333333333, green: 0.8823529412, blue: 0.9803921569, alpha: 1)
        courseTwoCutLabel.textColor = #colorLiteral(red: 0.7333333333, green: 0.8823529412, blue: 0.9803921569, alpha: 1)
        courseOneCutLabel.text = "Course 1 cut:"
        courseTwoCutLabel.text = "Course 2 cut:"
        
        // Popup alert
        if result.0 < 36 || result.1 < 36 
            // First instantiate the UIAlertController
            let alert = UIAlertController(title: "Small cut!", message: "Depending on conditions you may be able to open or squeeze head joints to eliminate this small cut.", preferredStyle: .alert)
            
            // Add action buttons to it and attach handler functions if you want to
            alert.addAction(UIAlertAction(title: "Got it!", style: .cancel, handler: nil))
            
            // Show the alert by presenting it
            self.present(alert, animated: true)
        
     else 
        let result = cutCalc.imperialRunningBondCalc(overallLength: Double(overallLength.text!) ?? 1.0, unitLength: Double(unitLength.text!) ?? 1.0, headJointSize: Double(headJointSize.text!) ?? 1.0)
        courseOneCut.text = String("\(result.0) inches")
        courseTwoCut.text = String("\(result.1) inches")
        buttonSelectorLabel.text = "Running bond cut results"
        courseTwoCut.textColor = #colorLiteral(red: 0.7333333333, green: 0.8823529412, blue: 0.9803921569, alpha: 1)
        courseTwoCutLabel.textColor = #colorLiteral(red: 0.7333333333, green: 0.8823529412, blue: 0.9803921569, alpha: 1)
        courseOneCutLabel.text = "Course 1 cut:"
        courseTwoCutLabel.text = "Course 2 cut:"
        
        // Popup alert
        if result.2 < 2.5 || result.3 < 2.5 
            // First instantiate the UIAlertController
            let alert = UIAlertController(title: "Small cut!", message: "Depending on conditions you may be able to open or squeeze head joints to eliminate this small cut.", preferredStyle: .alert)
            
            // Add action buttons to it and attach handler functions if you want to
            alert.addAction(UIAlertAction(title: "Got it!", style: .cancel, handler: nil))
            
            // Show the alert by presenting it
            self.present(alert, animated: true)
        
    


// Height cut button- DONE
@IBAction func heightCut(_ sender: Any) 
    // Resigns keyboard once button pressed
    self.view.endEditing(true)
    
    // Activate methods for cut calculation metric/imperial & label changes
    if unitHeightMeasurement == true 
        let result = cutCalc.metricHeightCutCalc(overallHeight: Double(overallHeight.text!) ?? 1.0, unitHeight: Double(unitHeight.text!) ?? 1.0, beadJointSize: Double(bedJointSize.text!) ?? 1.0)
        heightCutResult.text = String("\(result) mm")
        
        // Popup alert
        if result < 30.5 
            // First instantiate the UIAlertController
            let alert = UIAlertController(title: "Small cut!", message: "Depending on conditions you may be able to gain or squeeze to eliminate this small cut.", preferredStyle: .alert)
            
            // Add action buttons to it and attach handler functions if you want to
            alert.addAction(UIAlertAction(title: "Got it!", style: .cancel, handler: nil))
            
            // Show the alert by presenting it
            self.present(alert, animated: true)
        
     else 
        let result = cutCalc.imperialHeightCutCalc(overallHeight: Double(overallHeight.text!) ?? 1.0, unitHeight: Double(unitHeight.text!) ?? 1.0, beadJointSize: Double(bedJointSize.text!) ?? 1.0)
        heightCutResult.text = String("\(result) inches")
        // Popup alert
        if result.1 < 2.5 
            // First instantiate the UIAlertController
            let alert = UIAlertController(title: "Small cut!", message: "Depending on conditions you may be able to gain or squeeze to eliminate this small cut.", preferredStyle: .alert)
            
            // Add action buttons to it and attach handler functions if you want to
            alert.addAction(UIAlertAction(title: "Got it!", style: .cancel, handler: nil))
            
            // Show the alert by presenting it
            self.present(alert, animated: true)
        
    


// Length cut calculator metric imperial selector- DONE
@IBAction func lengthUnitSelector(_ sender: UISegmentedControl) 
    if (metricImperialLengthSelector.selectedSegmentIndex == 0) 
        unitLengthMeasurement = true
     else 
        unitLengthMeasurement = false
    


// Height cut calculator metric imperial selector- DONE
@IBAction func heightUnitSelector(_ sender: UISegmentedControl) 
    if (metricImperialHeightSelector.selectedSegmentIndex == 0) 
        unitHeightMeasurement = true
     else 
        unitHeightMeasurement = false
    


// Acts to dismiss number keyboard when user taps outside
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) 
    overallLength.resignFirstResponder()
    unitLength.resignFirstResponder()
    headJointSize.resignFirstResponder()
    overallHeight.resignFirstResponder()
    unitHeight.resignFirstResponder()
    bedJointSize.resignFirstResponder()

这是我的储蓄课程:

// Cut calculator saved states
static var overallLengthKey = "OverallLength"
static var unitLengthKey = "UnitLength"
static var headJointSizeKey = "HeadJointSize"
static var unitLengthSelector = "LengthUnitSelector"

static var overallHeightKey = "OverallHeight"
static var unitHeightKey = "UnitHeight"
static var bedJointSizeKey = "BedJointSize"
static var unitHeightSelector = "HeightUnitSelector"

// Saving user data
static func saveLengthCutCalcState(overallLength: Double, unitLength: Double, headJointSize: Double, measurementLengthSelector: Int) 
    
    let defaults = UserDefaults.standard
    defaults.set(overallLength, forKey: overallLengthKey)
    defaults.set(unitLength, forKey: unitLengthKey)
    defaults.set(headJointSize, forKey: headJointSizeKey)
    defaults.set(measurementLengthSelector, forKey: unitLengthSelector)
    defaults.synchronize()

// Saving user data
static func saveHeightCutCalcState(overallHeight: Double, unitHeight: Double, bedJointSize: Double, measurementHeightSelector: Int) 

    let defaults = UserDefaults.standard
    defaults.set(overallHeight, forKey: overallHeightKey)
    defaults.set(unitHeight, forKey: unitHeightKey)
    defaults.set(bedJointSize, forKey: bedJointSizeKey)
    defaults.set(measurementHeightSelector, forKey: unitHeightSelector)


// Retrieve user data
static func retrieveCutCalcValue(key: String) -> Any? 
    
    let defaults = UserDefaults.standard
    
    defaults.synchronize()
    
    return defaults.value(forKey: key)


// Clear state
static func clearLengthCutCalcState() 
    
    let defaults = UserDefaults.standard
    
    // Clear the saved state data in user defaults
    defaults.removeObject(forKey: overallLengthKey)
    defaults.removeObject(forKey: unitLengthKey)
    defaults.removeObject(forKey: headJointSizeKey)
    defaults.removeObject(forKey: unitLengthSelector)

static func clearHeightCutCalcState() 
    
    let defaults = UserDefaults.standard
    
    // Clear the saved state data in user defaults
    defaults.removeObject(forKey: overallHeightKey)
    defaults.removeObject(forKey: unitHeightKey)
    defaults.removeObject(forKey: bedJointSizeKey)
    defaults.removeObject(forKey: unitHeightSelector)

// DONE- Even gauge calcualtor saved states -DONE

【讨论】:

以上是关于用户默认不保存,不检索数据的主要内容,如果未能解决你的问题,请参考以下文章

如何保存响应 JSON 数组并在 swift 中使用用户默认值进行检索

如何在swift中将json数组保存和检索到用户默认值?

将用户默认设置保存到 iCloud

NSUserDefaults 没有正确保存

VI命令----用于检索

cookies不设置过期时间默认是永远不过期吗