我有一个Settings类,它有一堆UITextField
属性,每个属性都要转换为Decimal变量。该部分全部有效,我使用NSUserDefaults
保存UITextField
内容和变量Decimals。 “设置”视图控制器正确加载并显示我之前保存的UITextField
内容。
问题是我需要在没有设置视图控制器加载的情况下访问可选变量,当我尝试通过didFinishLaunchingWithOptions
中的AppDelegate
方法加载它们时,它总是返回nil。如果我在NSUserDefaults
中加载变量(来自viewDidLoad()
),或者在实际的“设置”视图控制器上加载变量,则值会正确显示。
我对Swift来说还是一个新手,但是我在这里忽略了什么想法?
编辑:我为延迟道歉;刚回到城里,我附上了解决我问题的代码。我还有一些我想要分配的变量,但是一旦我弄明白了,剩下的就很容易了。截至目前,我甚至无法从视图控制器加载变量值。我知道当我编辑文本字段时,相应的Decimal?
变量随之变化,它不会被保存或加载。我保存到UITextField
的{{1}}是正确的节省/加载,而不是NSUserDefaults
来自Decimal?
值的UITextField
变量。
class SettingsViewController: UIViewController {
var concrete360v: Decimal?
@IBOutlet var concrete360: UITextField?
//save the text field values to NSUserDefaults
func saveTextField(textField: String?, key: String) {
let defaults = UserDefaults.standard
defaults.set(textField, forKey: key)
}
//load text field value saved to NSUserDefault
func loadDefault(textField: UITextField?, fieldName: String) {
if let text = UserDefaults.standard.object(forKey: fieldName) as? String
{
textField?.text = text
}
}
//assign the text field values to their respective value variables
func assignTextToValue(textField: UITextField?, valueField: inout Decimal?)
{
if let text = textField?.text {
valueField = Decimal(string: text)
print("\(String(describing: concrete360v))")
}
}
//save the value variable to NSUserDefaults
func saveValue(value: Decimal?, key: String) {
let defaults = UserDefaults.standard
defaults.set(value, forKey: key)
}
//load value variable from NSUserDefaults
func loadValue(value: inout Decimal?, valueName: String) {
if let loadedValue = UserDefaults.standard.object(forKey: valueName) as? Decimal {
value = loadedValue
}
}
//save ALL value variables to NSUserDefaults
func saveAllValues() {
saveValue(value: concrete360v, key: "concrete360v")
print("Saved all your variables")
}
//load ALL value variables from NSUserDefaults
func loadAllValues() {
loadValue(value: &concrete360v, valueName: "concrete360v")
print("\(String(describing: concrete360v))")
}
override func viewDidAppear(_ animated: Bool) {
loadDefault(textField: concrete360, fieldName: "concrete360")
}
override func viewWillAppear(_ animated: Bool) {
loadValue(value: &concrete360v, valueName: "concrete360v")
print("\(String(describing: concrete360v))")
}
答案 0 :(得分:1)
macOS和iOS上的CFPreferences
/ UserDefaults
系统只能存储几种类型的对象。由于UserDefaults
类的实际实现是在Objective-C(其名称为NSUserDefaults
)中,因此这些类型都是Objective-C类型。在默认系统中读取和写入数值所涉及的类型称为NSNumber
,它基本上只是围绕C&C的原始整数和浮点类型的面向对象的包装器。大多数Swift整数和浮动类型都可以通过Swift< - > Objective-C桥接魔法强制转换为NSNumber
,这就是为什么你可以发送类似Int
或Double
的内容到UserDefaults
的方法,并期望它的工作。但是,Decimal
稍有不同,因为它与Objective-C桥接为NSDecimalNumber
,它是NSNumber
的子类。由于它符合NSNumber
的API,UserDefaults
能够将值写入默认系统;但是,默认系统的底层存储不保留原始类,而只是写入数值。因此,如果您将1.23
写为Decimal
,则会写入数字1.23
,但当您将其读回时,UserDefaults
会看到1.23
存储,并将其导入为花园种类NSNumber
,可能包含Double
。
如果你进行一点测试,你可以看到这个:
import Foundation
let foo = 2.3 as Decimal
UserDefaults.standard.set(foo, forKey: "Foo")
print(type(of: UserDefaults.standard.value(forKey: "Foo")!))
输出:
__NSCFNumber
是NSNumber
的私有子类(存在是为了促进NSNumber
和CFNumberRef
之间的免费桥接,但这超出了此答案的范围)
无论如何,NSNumber
神奇地连接到大多数标准Swift整数和浮点类型,但是系统没有魔法将它桥接到Decimal
。您可以将NSDecimalNumber
投射到Decimal
,但可以使用普通的NSNumber
进行尝试,然后获得nil
。欢迎来到Swift< - > Objective-C桥的野性,毛茸茸的世界。
无论如何,您仍然可以通过转换而不是转换来将其转换为Decimal
:
import Foundation
let foo = 2.3 as Decimal
UserDefaults.standard.set(foo, forKey: "Foo")
if let bar = UserDefaults.standard.value(forKey: "Foo") as? Double {
let baz = Decimal(bar)
print("got baz: \(baz)")
} else {
print("bar is nil")
}
输出:
got baz: 2.3