1

Is an NSValueTransform subclass a good choice for displaying Core Data attributes into UI views displaying:

  1. A number string like (0,1,2,3,etc) into a string such as (Pending, Completed, Frozen, In progress, etc)
  2. A number string like (0,1) into a app-based image (red.png if 0, green.png if 1)

Here's what Core Data displays for the two attributes, timer and status:

enter image description here

Here is what I want to be displayed instead, without changing the values in Core Data:

enter image description here

If not to use NSValueTransformer, in what other way is this possible?

I do not want to see the data permanently converted, only for the benefit of less data stored in Core Data and better UI view items.

I have also tried to modify the attributes in the managed object class (with out KVO notification) with no luck.

David
  • 3,285
  • 1
  • 37
  • 54
  • 1
    I don't think so, for either 1 or 2. Value transformers are great for stuffing objects into core data, and they're good for transforming strings to strings and numbers and all that. But #1 you'd associate with a state attribute in your entity, and there's not reason to not have the model be explicit, and have the view transform it to something readable, rather than basically burying it in the accessor accessories, which is what a value transform is here. When I use them, I never find them right away, buried off somewhere away from where they matter. – stevesliva Mar 17 '15 at 04:26
  • 1
    Also, in terms of efficient storage for enumerated types, there's [this answer about enums in Core Data](http://stackoverflow.com/a/13224056/3120884) – stevesliva Mar 17 '15 at 04:27
  • Actually, it's not bad… I just added an NSValueTransform to an NSTableView where I was previously just displaying the enum's int value… and it worked great! – geowar Mar 17 '15 at 17:50
  • @geowar Did you implement both transform and reverse transform? In what class are those methods written? I am not seeing how it's possible to transform string to image and later image back to string. – David Mar 20 '15 at 22:55
  • You shouldn't need a reversible transform. Are those views modifiable by the user? How would that work? – Ken Thomases Mar 21 '15 at 00:02
  • @KenThomases No. I want the string (0,1,2,etc) to be **displayed** as one of 2 images or as a full string message. – David Mar 21 '15 at 01:00
  • @david I didn't need the reverse… If I did and the cases were limited I'd probably use a popup menu (and not need the transform… except to populate the popup?) – geowar Mar 21 '15 at 17:50
  • @geowar Thanks. I am experimenting with Ken Thomases suggestion of -tableView: viewForTableColumn: row: This looks to be much less complicated. – David Mar 21 '15 at 19:38

2 Answers2

1

Yes, NSValueTransformer subclasses work just fine for this purpose.

You can also add read-only computed properties to your managed object class, and that should work, too. Those properties can even be added by a category in the controller code, if they don't make sense as part of the model code.

For example:

+ (NSSet*) keyPathsForValuesAffectingStatusDisplayName
{
    return [NSSet setWithObject:@"status"];
}
- (NSString*) statusDisplayName
{
    NSString* status = self.status;
    if ([status isEqualToString:@"0"])
        return @"Pending";
    else if ([status isEqualToString:@"1"])
        return @"Completed";
    // ...
}

The +keyPathsForValuesAffectingStatusDisplayName method lets KVO and bindings know that when status changes, so does this new statusDisplayName property. See the docs for +keyPathsForValuesAffectingValueForKey: to learn how that works.

Ken Thomases
  • 88,520
  • 7
  • 116
  • 154
  • And then we add this ValueTranformer class to the binding for those cells? Thank you for suggesting code. I will work on the new class and read-only and reply back tomorrow. – David Mar 21 '15 at 01:27
  • Do you know of another technique to leave the attribute values unchanged while displaying something else? – David Mar 21 '15 at 01:38
  • 1
    Yes, you'd add the value transformer to the binding for those cells. There are probably any number of approaches to displaying something different, like using a custom view class for the cell, or setting up the cell in `-tableView:viewForTableColumn:row:`, or whatever. – Ken Thomases Mar 21 '15 at 01:53
1

I ended up using what at first appeared to be blocking the display of different info in those cells, using:

#pragma mark - Table View Delegate

- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
    /*  tableColumn = (string) @"AutomaticTableColumnIdentifier.0"
                row = (int)    0          */
    NSString *identifier = [tableColumn identifier];
    NSTableCellView *cellView = [tableView makeViewWithIdentifier:identifier owner:self];

    NSManagedObject *item = [self.itemArrayController.arrangedObjects objectAtIndex:row];

    if ([identifier isEqualToString:@"AutomaticTableColumnIdentifier.0"]) {
        /*  subviews returns array with  0 = Image View &
                                         1 = Text Field      */
        /*  First, set the correct timer image  */
        ...  logic  ...
        NSImageView *theImage = (NSImageView *)[[cellView subviews] objectAtIndex:0];
        theImage.image = [NSImage imageNamed:@"green.gif"];

        /*  Second, display the desired status  */
        NSTextField *theTextField = (NSTextField *)[[result subviews] objectAtIndex:1];
        ...  logic  ...
        theTextField.stringValue = @"Pending";
    }

    return cellView;
}

Apple's documentation states (somewhere) that bindings with an Array Controller can work in combination with manually populating the table view cells. It seems best and easiest to start with bindings and then refine display values manually.

David
  • 3,285
  • 1
  • 37
  • 54