防止NSTableView在用户填写值之前保存新行 [英] Prevent NSTableView from saving new row until user fills values

查看:72
本文介绍了防止NSTableView在用户填写值之前保存新行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  struct预设:可编码{
var名称:字符串
var值: Int
}

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

此外,为了便于其余代码访问这些预设,我创建了一个具有属性 presets [Preset] 的c>实现了计算得到的getter和setter来从UserDefaults中检索它。



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



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



编辑:在这个问题上,我似乎一个人注意:参见项目,以及线程。



但是,第二个问题是最严重的问题:当我按下 + 按钮将新行添加到我的 NSTableView 时,最初,名称和值列为空,直到用户向其中添加一些文本/值。但是,绑定将新的空行(没有 name value 属性)保存到 UserDefaults 。可以通过注释 printPresets()中的代码来看到。例如,在添加新行之前,这是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>预设< / key>
< array>
< dict>
< key> name< / key>
< string>第一个预设< / string>
< key> value< / key>
< integer> 1< / integer>
< / dict>
< dict>
< key> name< / key>
< string>第二个预设< / 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>预设< / key>
< array>
< dict>
< key> name< / key>
< string>第一个预设< / string>
< key> value< / key>
< integer> 1< / integer>
< / dict>
< dict>
< key> name< / key>
< string>第二个预设< / string>
< key> value< / key>
< integer> 2< / integer>
< / dict>
< dict />
< / array>
< / dict>
< / plist>

请注意多余的< dict /> 在第二个预设之后。



如果在设置处于此状态的同时运行getter,并且该行为空,则getter会尝试从plist读取此行,由于缺少名称 value 属性。具体来说,我收到以下错误:

 线程1:致命错误: try!表达式意外引发错误:
Swift.DecodingError.keyNotFound(CodingKeys(stringValue: name,
intValue:nil),Swift.DecodingError.Context(codingPath:
[_PlistKey(stringValue: Index 2,intValue:2 )],debugDescription:
与键CodingKeys(stringValue:\ name\,
intValue:nil)(\ name\)。不相关的值,底层错误:nil ))

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


  1. 我尝试设置 sharedUserDefaultsController。立即应用 false ,添加保存按钮并将其绑定到 sharedUserDefaultsController save:操作。执行此操作时,不会加载我的预设。另一方面,我可以使用 + 按钮添加新行,而不会立即导致应用崩溃。只要在按下保存按钮之前在该行中添加名称和值,该应用程序就不会崩溃。但是,如果我尝试保存时未填写名称或值字段,或者都不填写两者,则该应用程序仍会崩溃。因此,不是一个好的解决方案。除非字段填写正确,否则是否可以将保存按钮设为灰色?但是话又说回来,如果都一样的话,我想在此窗口中使用节省范式。


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



解决方案

我通过在<属性>的属性检查器中的对象控制器下更改类名来解决此问题code> NSArrayController 到我的 MyModuleName.Preset 中,现在声明为类:

  class预设:NSObject,可编码{
let名称:字符串
let值:Int

覆盖init(){
self.name =一个预置
self.setpoint = 1
super.init()
}

init(name:String,value:Int ){
self.name =名称
self.value =值
super.init()
}
}

通过在 init()(不带参数的版本)中提供默认值,当出现新行时是cre吃完后,它会填充这些默认值,从而使问题消除。


I'm working on a preferences window for my app, where the user can add and remove presets using NSTableView. A preset is defined as:

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

These presets should be persisted to UserDefaults. My goal is to write as little code as possible, and what little is written should be generic code, so my project makes use of NSUserDefaultsController, NSArrayController and Cocoa Bindings. Seeing as array, string and integer types are available in the property lists used by UserDefaults to store these settings, I've decided to use these rather than opaque serialized data types.

Also, to facilitate access to these presets by the rest of the code, I created a Settings singleton object with a property presets of type [Preset] which implements computed getters and setters to retrieve this from UserDefaults.

I've uploaded an MCVE to GitHub. It logs the value of the presets property every second to demonstrate the issues I need help with -- other than these two issues, I believe everything is working fine.

The first issue is that I'd like to move to a view-based content mode in my NSTableView, but when I make this change and restore the other , any edits made to entries of the NSTableView are not saved to UserDefaults. They work fine in cell-based content mode, which is what I'm using in the MCVE. Since I'm a Cocoa/Swift newbie, I'd really appreciate step-by-step instructions about what needs to be changed in this project in order to migrate to view-based content mode, while keeping the functionality of editing entries of the NSTableView.

EDIT: Looks like I'm note alone in this issue: see this project, as well as this thread in a Cocoa mailing list.

However, the second issue is the most serious one: when I press the + button to add a new row to my NSTableView, initially the "name" and "value" columns are empty until the user adds some text/values to it. However, the bindings save a new empty row (without name and value properties) to UserDefaults. This can be seen by commenting out the code in printPresets(). For instance, before adding a new row, this is the content of the 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>
    </array>
</dict>
</plist>

After pressing +, this is the new content of the 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>

Note the extra <dict/> after the second preset.

If the getter runs while the settings are in this state, with an empty row, the getter tries to read in this row from the plist and fails due to the lack of name and value properties. Specifically, I get the following error:

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))

I have no idea how to fix this. I've tried one thing, unsuccesfully, and thought up of another, which I could use some advice on how to implement. I'd also appreciate if other alternatives that I didn't think of were suggested.

  1. I tried setting sharedUserDefaultsController.appliesImmediately to false, adding a "Save" button and binding it to the sharedUserDefaultsController's save: action. When I do this, my presets aren't loaded. On the other hand, I can add a new row with the + button without immediately crashing the app. As long as I add a name and a value to this row before pressing the "Save" button, the app doesn't crash at all. However, if I try to save without filling in either the name or the value fields, or both, the app still crashes. So, not a good solution. Perhaps if I could gray out the "Save" button unless the fields were properly filled? But then again, if it's all the same, I'd like to use a "save-less" paradigm for this window.

  2. Another possibility that I see, and which is somewhat reasonable, would be to fill the "name" and "value" fields with some default data when a new row is created. I found the "Placeholder" attribute, but it's not suitable: it's just a suggestion for the user, and until the user actually fills in something, the fields are left blank and not written to the plist.

解决方案

I solved this by changing the "Class Name" under "Object Controller" in the Attributes Inspector for NSArrayController to my MyModuleName.Preset, now declared as a class:

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()
    }
}

By providing defaults values in init() (the version without arguments), when a new row is created, it is filled with these default values, making the problem go away.

这篇关于防止NSTableView在用户填写值之前保存新行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆