0

I've a class with two properties: NSString *fileName, and NSDictionary *frames illustrated below.

  • NSDictionary *frames
    • key: NSString
    • value: instance of fields dictionary
  • NSDictionary *fields
    • key: NSNumber (C++ enum value)
    • value: NSValue (C++ object pointer)

I want to create a master-detail view with an NSTableView listing each file. fileName in the first column, which is easy enough to bind, but I can't figure out how to bind any columns, nor fields in the detail view, to values in the fields sub-dictionaries.

I guess I can't bind to C++ object methods anyway. I'm thinking I need a custom controller for the value in the fields sub-dictionary. Would it be possible to have the custom controller adhere to KVC such that I can access field values by enum key, calling methods appropriate to the type defined by the enum per the C++ library I'm using?

Or, should I store the data from each field as the value in the fields dictionary? After modifying the value in the GUI, I'd have to run a method to process the dictionaries anyway, at which point I can reconstruct the C++ fields object and call the appropriate methods.

Regardless, my challenge is figuring out bindings for a nested dictionary.

Matt
  • 1,041
  • 1
  • 16
  • 28

2 Answers2

1

Subclassing NSController and friends is not likely to be the best approach here.

There's no way around it: Key/Value Coding, the API that all of bindings is based upon relies on the keys/keyPaths being NSStrings, not NSNumbers (or any other types). You would probably find this much easier if you switched away from using NSNumber keys to using NSString keys. Getting there could be as simple as implementing two functions with switch statements that translate back and forth between strings and enum values.

You've already realized there's no hope for binding against a pointer to a C++ object, so the following discussion assumes you've baked the values into the dictionary. If, for some reason, you can't switch to using NSString keys in your fields dictionary, you could also subclass NS[Mutable]Dictionary and override -valueForUndefinedKey: and -setValue:forUndefinedKey: to perform this translation. Subclassing NS[Mutable]Dictionary is not for the faint of heart, being that those classes are backed by a class cluster. The documentation clearly enumerates all the requirements for subclassers, so I won't cover that here. Additionally, you would need to, in your -setObject:forKey: implementation, be sure to send -willChangeValueForKey: and -didChangeValueForKey: notifications for the string keys that corresponded to modifications made with the NSNumber keys.

Assuming you would ultimately prefer to store all this in a std::hash_map<enum, ptr> or some other C++ class/struct, the logical approach to bridge to Objective-C would be to create a new model class that wrapped the STL hash_map and provided KVC/KVO semantics using NSString keys, handling the translation similarly to what's described above.

ipmcc
  • 29,581
  • 5
  • 84
  • 147
  • I figured out the NSString vs. NSNumbers thing after having posted my question. I ended up going by my last suggestion - storing the data from each field as the values in the sub-dictionary. I think it's now just a matter of figuring out which controller objects to use, how to link them to views, and how to configure the views with the correct keys, etc. – Matt Dec 05 '12 at 16:40
0

I ended up storing the data for each field as the value in the "fields" sub-dictionary. I didn't realize I could specify a key-path to bind to the value.

For example, I have a class with a member NSArray "tracks" containing a set of objects each with member NSDictionary "frames" as described in my question. "tracks" is represented in my GUI with an NSArrayController to which is bound an NSTableView.

Within the NSTableView I can bind the static text of the table cells to the Table Cell View with model key path "objectValue.frames.key1.key2".

Likewise, within the detail part of the master-detail view, I can bind text field cells to the NSArrayController with "selection" as controller key, and "frames.key1.key2" as model key path.

Matt
  • 1,041
  • 1
  • 16
  • 28