我正在为我的应用设置首选项窗口,用户可以在其中使用NSTableView
添加和删除预设。预设定义为:
struct Preset: Codable {
var name: String
var value: Int
}
这些预设应保留到UserDefaults
。我的目标是编写尽可能少的代码,而编写的代码应该很少是通用代码,因此我的项目使用了NSUserDefaultsController
,NSArrayController
和Cocoa Bindings。在UserDefaults
用来存储这些设置的属性列表中可以看到数组,字符串和整数类型,因此,我决定使用这些而不是不透明的序列化数据类型。
此外,为了方便其余代码访问这些预设,我创建了一个类型为presets
的属性[Preset]
的Settings单例对象,该对象实现了计算的getter和setter以便从UserDefaults中检索此对象。
我已将MCVE上传到GitHub。它每秒记录一次presets
属性的值,以演示我需要帮助的问题-除了这两个问题,我相信一切都很好。
第一个问题是我想在我的NSTableView
中转到基于视图的内容模式,但是当我进行此更改并恢复另一个更改时,对{{1 }}未保存到NSTableView
。它们在基于单元的内容模式下可以正常工作,这是我在MCVE中使用的模式。由于我是Cocoa / Swift的新手,因此,我非常感谢您逐步说明有关该项目中需要更改的内容,以便迁移到基于视图的内容模式,同时保留编辑项目的功能。 UserDefaults
。
编辑:看起来我在这个问题上一个人被注意到:请参阅this项目以及Cocoa邮件列表中的this线程。
但是,第二个问题是最严重的问题:当我按下NSTableView
按钮向+
添加新行时,最初的“名称”和“值”列为空,直到用户向其中添加一些文本/值。但是,绑定将新的空行(没有NSTableView
和name
属性)保存到value
。可以通过注释掉UserDefaults
中的代码来看到。例如,在添加新行之前,这是plist的内容:
printPresets()
按<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>presets</key>
<array>
<dict>
<key>name</key>
<string>First preset</string>
<key>value</key>
<integer>1</integer>
</dict>
<dict>
<key>name</key>
<string>Second preset</string>
<key>value</key>
<integer>2</integer>
</dict>
</array>
</dict>
</plist>
后,这是plist的新内容:
+
请注意第二个预设后的多余<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>presets</key>
<array>
<dict>
<key>name</key>
<string>First preset</string>
<key>value</key>
<integer>1</integer>
</dict>
<dict>
<key>name</key>
<string>Second preset</string>
<key>value</key>
<integer>2</integer>
</dict>
<dict/>
</array>
</dict>
</plist>
。
如果在设置处于此状态的状态下运行吸气剂,并且该检测器的行为空,则该吸气剂将尝试从plist读取此行,并且由于缺少<dict/>
和name
属性而失败。具体来说,我收到以下错误:
value
我不知道该如何解决。我尝试过一件事,未成功,但想到了另一件事,可以就如何实现使用一些建议。如果有人提出其他我没想到的替代方法,我也将不胜感激。
我尝试将Thread 1: Fatal error: 'try!' expression unexpectedly raised an error:
Swift.DecodingError.keyNotFound(CodingKeys(stringValue: "name",
intValue: nil), Swift.DecodingError.Context(codingPath:
[_PlistKey(stringValue: "Index 2", intValue: 2)], debugDescription:
"No value associated with key CodingKeys(stringValue: \"name\",
intValue: nil) (\"name\").", underlyingError: nil))
设置为sharedUserDefaultsController.appliesImmediately
,添加一个“保存”按钮并将其绑定到false
的{{1}}操作中。执行此操作时,不会加载我的预设。另一方面,我可以使用sharedUserDefaultsController
按钮添加新行,而不会立即导致应用崩溃。只要在按下“保存”按钮之前在该行中添加名称和值,该应用程序就不会崩溃。但是,如果我尝试保存时未填写名称或值字段,或者都不填写两个字段,则该应用仍会崩溃。因此,不是一个好的解决方案。除非字段填写正确,否则是否可以将“保存”按钮设为灰色?但是话又说回来,如果都一样的话,我想在此窗口中使用“节省”范式。
我看到的另一种可能性在某种程度上是合理的,那就是在创建新行时用一些默认数据填充“名称”和“值”字段。我找到了“占位符”属性,但不合适:这只是对用户的建议,在用户实际填写内容之前,这些字段留为空白并且未写入plist。
答案 0 :(得分:0)
我通过将NSArrayController
的属性检查器中“对象控制器”下的“类名”更改为现在声明为类的MyModuleName.Preset
来解决此问题:
class Preset: NSObject, Codable {
let name: String
let value: Int
override init() {
self.name = "A preset"
self.setpoint = 1
super.init()
}
init(name: String, value: Int) {
self.name = name
self.value = value
super.init()
}
}
通过在init()
(不带参数的版本)中提供默认值,在创建新行时,将使用这些默认值填充新行,从而使问题消除了。