实现 NSOutlineView 来编辑 plist 文件的内容
我正在使用 cocos2d-iphone 编写一个游戏,我们的阶段是在 .plist 文件中定义的。然而,文件变得越来越大 - 因此我开发了一个编辑器,它为流程添加了一些结构,并将大部分 plist 分解为固定字段。但是,某些元素仍然需要 plist 编辑器类型功能,因此我在显示“其他参数”的面板上实现了 NSOutlineView。我正在尝试复制 XCode 的“属性列表编辑器”的功能。
我已经实现了以下系统; http://www.stupendous.net/archives/2009/01/ 11/nsoutlineview-example/
这非常接近我的需要,但是有一个问题我今天花了大部分时间试图解决。通过查找父字典并使用,从所选项目“向后”计算键列的值;
return [[parentObject allKeysForObject:item] objectAtIndex:0];
但是,当树中给定字典中有多个具有相同值的项目时,此语句始终返回具有该值的第一个项目(它似乎使用 isEqualToString: 或哈希值来比较字符串)。这导致键列显示“item1、item1、item1”,而不是 item1、item2、item3(其中 item 1-3 都具有值“”)。我接下来尝试了;
-(NSString*)keyFromDictionary:(NSDictionary*)dict forItem:(id)item
{
for( uint i = 0; i < [[dict allKeys] count]; i++ ) {
id object = [dict objectForKey:[[dict allKeys] objectAtIndex:i]];
if ( &object == &item ) {
return [[dict allKeys] objectAtIndex:i];
}
}
return nil;
}
但这总是返回nil。我希望对 NSOutlineView 有更多经验的人能够提供更好的解决方案。虽然这个问题只在链接的示例中出现一次,但我在从字典中删除项目时不得不多次使用这个问题。任何帮助将不胜感激。
I'm writing a game using cocos2d-iphone and our stages are defined in .plist files. However, the files are growing large - so I've developed an editor that adds some structure to the process and breaks most of the plist down into fixed fields. However, some elements still require plist editor type functionality, so I have implemented an NSOutlineView on the panels that show 'other parameters'. I am attempting to duplicate the functionality from XCode's 'Property List Editor'.
I've implemented the following system; http://www.stupendous.net/archives/2009/01/11/nsoutlineview-example/
This is very close to what I need, but there is a problem that I've spent most of today attempting to solve. Values for the key column are calculated 'backwards' from the selected item by finding the parent dictionary and using;
return [[parentObject allKeysForObject:item] objectAtIndex:0];
However, when there are multiple items with the same value within a given dictionary in the tree, this statement always returns the first item that has that value (it appears to compare the strings using isEqualToString: or hash values). This leads to the key column showing 'item1, item1, item1' instead of item1, item2, item3 (where items 1-3 all have value ''). I next tried;
-(NSString*)keyFromDictionary:(NSDictionary*)dict forItem:(id)item
{
for( uint i = 0; i < [[dict allKeys] count]; i++ ) {
id object = [dict objectForKey:[[dict allKeys] objectAtIndex:i]];
if ( &object == &item ) {
return [[dict allKeys] objectAtIndex:i];
}
}
return nil;
}
But this always returns nil. I was hoping that somebody with a bit more experience with NSOutlineView might be able to provide a better solution. While this problem only appears once in the linked example, I've had to use this a number of times when deleting items from dictionaries for instance. Any help would be greatly appreciated.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
嗯,是的。这就是你告诉它要做的事情:“给我这个值的所有键;获取数组中的第一项;返回那个”。
不是那个声明在做这件事;而是在做这件事。这就是字典的工作原理:每个键只能在字典中出现一次,并且只能有一个对象作为其值,这是通过使用键的哈希值并通过发送键
isEqual:
消息来强制执行的(不是特定于 NSString 的isEqualToString:
——键不需要是字符串*)。另一方面,这些值并不是唯一的。任意数量的键可以具有相同的值。这就是为什么从值到键(尤其是到a键)如此困难的原因。
*无论如何,不在 NSDictionary 中。当您尝试生成 plist 输出时,如果字典包含任何非字符串键,它将失败。
这是该代码的最小问题。
首先,当迭代 NSArray 时,除非绝对需要,否则通常不应使用索引。使用快速枚举要干净得多。
其次,当您确实需要 NSArray 的索引时,正确的类型是 NSUInteger。如果可以的话,不要混合搭配类型。
第三,我不知道你对地址运算符的意思是什么,但你实际上所做的是获取这两个变量的地址。因此,您比较了局部变量
object
是否与参数变量item
是同一变量。由于它们不是同一个变量,因此该测试始终返回 false,这就是为什么您从不返回对象的原因 - 唯一的其他退出点返回nil
,所以这就是总是发生的情况。这段代码和之前的一行代码的问题在于,您试图从一个值转换为一个键,这与字典的工作方式相反:只有键是唯一的;只有键是唯一的。任意数量的键可以具有相同的值。
您需要使用其他东西作为物品。使用钥匙作为物品是一种方法;制作一个 模型对象 来表示每一行将是另一回事。
如果您采用模型对象路线,请不要忘记防止同一虚拟字典中的多行具有相同的键。 NSMutableSet 加上实现
hash
和isEqual:
会对此有所帮助。您可能还应该对数组的处理进行相同的更改。
Well, yeah. That's what you told it to do: “Get me all the keys for this value; get me the first item in the array; return that”.
It's not that statement that's doing it; it's how dictionaries work: Each key can only be in the dictionary once and can only have exactly one object as its value, and this is enforced using the hash of the key and by sending the keys
isEqual:
messages (not the NSString-specificisEqualToString:
—keys are not required to be strings*).The values, on the other hand, are not uniquified. Any number of keys can have the same value. That's why going from values to keys—and especially to a key—is so problematic.
*Not in NSDictionary, anyway. When you attempt to generate the plist output, it will barf if the dictionary contains any non-string keys.
That's the least of that code's problems.
First, when iterating on an NSArray, you generally should not use indexes unless you absolutely need to. It's much cleaner to use fast enumeration.
Second, when you do need indexes into an NSArray, the correct type is
NSUInteger
. Don't mix and match types when you can help it.Third, I don't know what you meant to do with the address-of operator there, but what you actually did was take the address of those two variables. Thus, you compared whether the local variable
object
is the same variable as the argument variableitem
. Since they're not the same variable, that test always returns false, which is why you never return an object—the only other exit point returnsnil
, so that's what always happens.The problem with this code and the earlier one-liner is that you're attempting to go from a value to a single key, which is contrary to how dictionaries work: Only the keys are unique; any number of keys can have the same value.
You need to use something else as the items. Using the keys as the items would be one way; making a model object to represent each row would be another.
If you go the model-object route, don't forget to prevent multiple rows in the same virtual dictionary from having the same key. An NSMutableSet plus implementing
hash
andisEqual:
would help with that.You probably should also make the same change to your handling of arrays.
为了澄清,我最终通过为 plist 文件中的每个集合创建代理对象(因此,为每个 NSMutableArray 或 NSMutableDictionary)解决了这个问题。这意味着我基本上镜像了 Plist 结构,并在每个级别包含了对原始对象的引用。这允许我存储每个对象的数组索引或字典键,因此当将项目从大纲视图保存回 Plist 结构时,我使用了代理对象上的“key”或“index”属性。
To clarify, I eventually resolved this problem by creating proxy objects for each of the collections in the plist file (so, for every NSMutableArray or NSMutableDictionary). This meant that I essentially mirrored the Plist structure and included references back to the original objects at each level. This allowed me to store the array index for each object or the dictionary key, so when saving items back from the outline view to the Plist structures, I used the 'key' or 'index' properties on the proxy object.