jpsasi
jpsasi

Reputation: 1905

ReactiveCocoa Enable NSButton based on NSTableView selection state

I have just started learning Reactive Cocoa. I am writing cocoa application where i want to enable the NSButton only if at least one row is selected in NSTableView.

I am using the following code in awakeFromNib

RACSignal *enableSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
    @strongify(self);
    if ([self.genreListTableView numberOfSelectedRows] > 0) {
        [subscriber sendNext:@YES];
    } else {
        [subscriber sendNext:@NO];
    }
    return nil;
}];

[self.addButton rac_liftSelector:@selector(setEnabled:)
                      withSignals:enableSignal,nil];

This code executed fine and made the addButton disabled during the app launch as there was no row selected Initially.

I want to make add Button enabled when ever TableView entry is selected. I don't know how to achieve in reactive way.

I could achieve the required behavior in the following manner.Not sure if this is the correct way to achieve it.

     [[self rac_signalForSelector:@selector(tableViewSelectionDidChange:)
                fromProtocol:@protocol(NSTableViewDelegate)] subscribeNext:^(RACTuple *value) {
    NSNotification *notification = value.first;
    if (self.genreListTableView == notification.object) {
        if ([self.genreListTableView numberOfSelectedRows] > 0) {
            [self.addButton setEnabled:TRUE];
        } else {
            [self.addButton setEnabled:FALSE];
        }
    }
}];

Any suggestion is appreciated to handle the requirement in reactive way.

Thank you

Upvotes: 4

Views: 1564

Answers (2)

Dmitry Seredinov
Dmitry Seredinov

Reputation: 21

Using Josh Caswell's approach, you can modify the observation as follows:

[[[RACObserve(self.genreListTableView, numberOfSelectedRows) distinctUntilChanged] deliverOn:[RACScheduler mainThreadScheduler]] subscribeNext:^(NSNumber* numSelected) {
    [self.addButton setEnabled:([numSelected integerValue] > 0)];
}];

Upvotes: 2

jscs
jscs

Reputation: 64002

You're on the right track. There are a few changes you should make to simplify.

First, use the RAC() macro to do simple bindings like this to a property.

RAC(self.addButton, enabled) = /* A signal */;

This will cause a compilation error because there is no declared property for enabled (and the getter is named isEnabled, not just `enabled). This can be fixed very simply with a category:

@interface NSButton (EnabledProp)

@property (assign, nonatomic, getter = isEnabled, setter = setEnabled:) BOOL enabled;

@end

Then, you can construct a signal directly from the table view's numberOfSelectedRows using another macro, RACObserve().

RAC(self.addButton, enabled) = RACObserve(self.genreListTableView, numberOfSelectedRows);

But that needs to be a BOOL for it to make sense as a binding. Transforming one type of value into another? That's a map:.

RAC(self.addButton, enabled) = [RACObserve(self.genreListTableView, numberOfSelectedRows) map:^id (NSNumber * numSelected){
                                          return @([numSelected integerValue] > 0);
}];

The NSInteger from numberOfSelectedRows is wrapped up in an NSNumber when it's in the signal, so unwrap it, compare to zero, and then wrap the comparison result back up.

Upvotes: 8

Related Questions