Reputation: 1905
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
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
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