阻止NSTableView在用户填写值之前保存新行

时间:2019-01-27 04:35:54

标签: swift macos nstableview cocoa-bindings

我正在为我的应用设置首选项窗口,用户可以在其中使用NSTableView添加和删除预设。预设定义为:

struct Preset: Codable {
    var name: String
    var value: Int
}

这些预设应保留到UserDefaults。我的目标是编写尽可能少的代码,而编写的代码应该很少是通用代码,因此我的项目使用了NSUserDefaultsControllerNSArrayController和Cocoa Bindings。在UserDefaults用来存储这些设置的属性列表中可以看到数组,字符串和整数类型,因此,我决定使用这些而不是不透明的序列化数据类型。

此外,为了方便其余代码访问这些预设,我创建了一个类型为presets的属性[Preset]的Settings单例对象,该对象实现了计算的getter和setter以便从UserDefaults中检索此对象。

我已将MCVE上传到GitHub。它每秒记录一次presets属性的值,以演示我需要帮助的问题-除了这两个问题,我相信一切都很好。

第一个问题是我想在我的NSTableView中转到基于视图的内容模式,但是当我进行此更改并恢复另一个更改时,对{{1 }}未保存到NSTableView。它们在基于单元的内容模式下可以正常工作,这是我在MCVE中使用的模式。由于我是Cocoa / Swift的新手,因此,我非常感谢您逐步说明有关该项目中需要更改的内容,以便迁移到基于视图的内容模式,同时保留编辑项目的功能。 UserDefaults

编辑:看起来我在这个问题上一个人被注意到:请参阅this项目以及Cocoa邮件列表中的this线程。

但是,第二个问题是最严重的问题:当我按下NSTableView按钮向+添加新行时,最初的“名称”和“值”列为空,直到用户向其中添加一些文本/值。但是,绑定将新的空行(没有NSTableViewname属性)保存到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

我不知道该如何解决。我尝试过一件事,未成功,但想到了另一件事,可以就如何实现使用一些建议。如果有人提出其他我没想到的替代方法,我也将不胜感激。

  1. 我尝试将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按钮添加新行,而不会立即导致应用崩溃。只要在按下“保存”按钮之前在该行中添加名称和值,该应用程序就不会崩溃。但是,如果我尝试保存时未填写名称或值字段,或者都不填写两个字段,则该应用仍会崩溃。因此,不是一个好的解决方案。除非字段填写正确,否则是否可以将“保存”按钮设为灰色?但是话又说回来,如果都一样的话,我想在此窗口中使用“节省”范式。

  2. 我看到的另一种可能性在某种程度上是合理的,那就是在创建新行时用一些默认数据填充“名称”和“值”字段。我找到了“占位符”属性,但不合适:这只是对用户的建议,在用户实际填写内容之前,这些字段留为空白并且未写入plist。

1 个答案:

答案 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()(不带参数的版本)中提供默认值,在创建新行时,将使用这些默认值填充新行,从而使问题消除了。