0

My application contains an NSTabView with two tabs. Further, the application itself has a playState which is an enum. The playState is kept in a Singleton.

typedef enum {
    kMyAppPlayStatePlaying,
    kMyAppPlayStatePaused
} MyAppPlayState;

The playState gets synthesized here.

@property (readwrite) MyAppPlayState playState;

I want to switch the NSTabView every time the playState changes. Therefore, I prepared an IBOutlet to add a binding similar to this one.

[self.playPauseTabView bind:@"selectedItemIdentifier" toObject:[MyAppState sharedState] withKeyPath:@"playState" options:nil];

I already recognized the identifier must be NSString. This does not match with my enum which is an int. I could maybe use an NSValueTransformer to fix this.
Further, selectedItemIdentifier does not exists. NSTabView only offers selectedTabViewItem which then allows to access identifier or label. Though, I cannot find a way to switch the item itself based on the identifer.

Monolo
  • 18,205
  • 17
  • 69
  • 103
JJD
  • 50,076
  • 60
  • 203
  • 339

2 Answers2

1

In situations like that, I find myself doing one of two things:

1) Register self (or some other object) as an observer of the property in question, and set the selected tab accordingly in -observeValueForKeyPath:ofObject:change:context:. It could look like this:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{ 
    if ( context == PlayStateChange )
    {
        if ( [[change objectForKey: NSKeyValueChangeKindKey] integerValue] == NSKeyValueChangeSetting )
        {
            NSNumber *oldValue = [change objectForKey: NSKeyValueChangeOldKey];
            NSNumber *newValue = [change objectForKey: NSKeyValueChangeNewKey];

            NSInteger oldInteger = [oldValue integerValue];
            NSInteger newInteger = [newValue integerValue];

            NSLog(@"Old play state: %ld, new play state: %ld", (long)oldInteger, (long)newInteger);

            // Do something useful with the integers here
        }

        return;
    }
}

2) declare a readonly NSString * property and declare that its value is affected by your playState property. Something like this:

@property (readonly) NSString *playStateStr;

// Accessor
-(NSString *)playStateStr
{
    return playState == kMyAppPlayStatePlaying ? @"playing" : "paused";
}

+(NSSet *)keyPathsForValuesAffectingPlayStateStr
{
    return [NSSet setWithObject: @"playState"];
}

Now you have an NSString-typed property that you can bind your tab view's selection.

Monolo
  • 18,205
  • 17
  • 69
  • 103
  • 1) As I understand, that means I add `[[MyAppState sharedState] addObserver:self forKeyPath:@"playState" options:NSKeyValueChangeSetting context:nil];`. But can I then retrieve the current state from `observeValueForKeyPath:ofObject:change:context:`? – JJD Sep 19 '11 at 16:02
  • Yes, it will be in the change dictionary with the key `NSKeyValueChangeNewKey`. Being an integer, it should be wrapped in an NSNumber. Notice the "should", haven't checked - I always use strings for this sort of thing. – Monolo Sep 19 '11 at 19:43
  • I am not sure if I understand. You wrote that you "doing one of two things". That means both pratices are independent from each other, right? So how can I retrieve the state value from `observeValueForKeyPath:ofObject:change:context:`? Can you add an example please? – JJD Sep 20 '11 at 11:55
  • Very cool. I got rid of the `NSValueTransformer` and everything works fine with 2 small methods: `addObserver:forKeyPath:options:context:` and `observeValueForKeyPath:ofObject:change:context:`. Thank you very much! – JJD Sep 21 '11 at 15:31
0

I forgot to connect the NSTabView with its IBOutlet in the Interface Builder.
The following works for me.

NSDictionary* playStateOptions = [NSDictionary dictionaryWithObject:[[PlayStateValueTransformer alloc] init] forKey:NSValueTransformerBindingOption];
[self.playPauseTabView bind:@"selectedLabel" toObject:[MyAppState sharedState] withKeyPath:@"playState" options:playStateOptions];

In the NSValueTransformer I return an NSString which must be set in Interface Builder for each tab!

JJD
  • 50,076
  • 60
  • 203
  • 339