Here is my answer copied from a GitHub issue:
I have not used ReactiveCocoaLayout , but I suspect that you may find that some of this code can be improved using RCL, in addition to RAC. I'm sure someone else will provide more details on this.
The first thing I suggest is to read -rac_signalForSelector: This is extremely valuable for the connection between delegate callbacks and the RAC signal.
For example, here, how you get signals that are your callbacks:
RACSignal *movedToParentController = [[self rac_signalForSelector:@selector(didMoveToParentViewController:)] filter:^(RACTuple *arguments) { return arguments.first != nil; // Ignores when parent is `nil` }]; RACSignal *beginSearch = [self rac_signalForSelector:@selector(searchBarShouldBeginEditing:)]; RACSignal *endSearch = [self rac_signalForSelector:@selector(searchBarShouldEndEditing:)];
Now let's say you have a method that updates the view:
- (void)updateViewInsets:(UIEdgeInsets)insets navigationBarAlpha:(CGFloat)alpha animated:(BOOL)animated { void (^updates)(void) = ^{ if (self.tableView.contentInset.top != insets.top) { self.tableView.contentInset = insets; self.tableView.scrollIndicatorInsets = insets; } self.navigationController.navigationBar.alpha = alpha; }; animated ? [UIView animateWithDuration:0.25 animations:updates] : updates(); }
Now you can use start to create several things.
First, since -searchBarShouldBeginEditing: is the shortest:
[beginSearch subscribeNext:^(id _) { UIEdgeInsets insets = UIEdgeInsetsMake(UIApplication.sharedApplication.statusBarFrame.size.height, 0, 0, 0); [self updateViewInsets:insets navigationBarAlpha:0 animated:YES]; }];
Now, for a more complicated piece. This signal composition starts with +merge with two signals, a signal for -didMoveToParentViewController: and a signal for searchBarShouldEndEditing: Each of these signals is mapped to the corresponding parent view controller and is associated with a boolean indicating whether to perform animation. This pair of values ββis packaged in RACTuple .
Next, using -reduceEach: tuple (UIViewController *, BOOL) mapped to the tuple (UIEdgeInsets, BOOL) . This mapping computes edge inserts from the parent view controller, but does not change the animated flag.
Finally, this signal composition is signed in which the helper method is called.
[[[RACSignal merge:@[ [movedToParentController reduceEach:^(UIViewController *parent) { return RACTuplePack(parent, @NO); // Unanimated }], [endSearch reduceEach:^(id _) { return RACTuplePack(self.parentViewController, @YES); // Animated }] ]] reduceEach:^(UIViewController *parent, NSNumber *animated) { CGFloat top = parent.topLayoutGuide.length; CGFloat bottom = parent.bottomLayoutGuide.length; UIEdgeInsets newInsets = UIEdgeInsetsMake(top, 0, bottom, 0); return RACTuplePack(([NSValue valueWithUIEdgeInsets:newInsets]), animated); }] subscribeNext:^(RACTuple *tuple) { RACTupleUnpack(NSValue *insets, NSNumber *animated) = tuple; [self updateViewInsets:insets.UIEdgeInsetsValue navigationBarAlpha:1 animated:animated.boolValue]; }];
In RAC you will find alternative approaches that you can use. The more you experiment, the more you discover what works, what doesn't work, nuances, etc.
PS. The corresponding @weakify / @strongify remains as an exercise.
NEXT ANSWER
Another approach is to use -rac_liftSelector: Here's how you can use it for the code you provided. This is very similar to the code above, except that you extract the animated flag into your own signal, instead of inserting it into the signal that calculates the inserts.
RACSignal *insets = [RACSignal merge:@[ [movedToParentController reduceEach:^(UIViewController *parent) { return parent; }], [endSearch reduceEach:^(id _) { return self.parentViewController; }] ]] map:^(UIViewController *parent) { CGFloat top = parent.topLayoutGuide.length; CGFloat bottom = parent.bottomLayoutGuide.length; UIEdgeInsets newInsets = UIEdgeInsetsMake(top, 0, bottom, 0); return [NSValue valueWithUIEdgeInsets:newInsets]; }]; RACSignal *animated = [RACSignal merge:@[ [movedToParentController mapReplace:@NO], [endSearch mapReplace:@YES], ]; RACSignal *alpha = [RACSignal return:@1]; [self rac_liftSelector:@selector(updateViewInsets:navigationBarAlpha:animated:) withSignals:insets, alpha, animated, nil];
IMO, no approach is a clear winner compared to another. However, recommendations recommend avoiding explicit subscription .