简体   繁体   中英

Swipe Cells with RAC

I'm building a swipeable cell adding a pan gesture to that cell. Basically, it has the same look and feel as the cells in Mailbox app, where you have a top view which you can swipe to the left or right to show another view (revealView) underneath.

I wanted to build this with the reactive approach so the way I am doing it is:

  • First, when I setup the view and the pan gesture, I'm filtering the rac_gestureSignal to get the current state of the gesture and update the top view position with bindings (some implementation details are simplified here) as well as hiding/showing the revealView when the gesture is ended/cancelled. I also call setNeedsLayout when either panDirection or revealView change (in order to update revealView frame accordingly) merging the signals from their values, as well as remove the reveal view on cell reusing:

     - (void)setupView { UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:nil]; panGesture.delegate = self; RACSignal *gestureSignal = [panGesture rac_gestureSignal], *beganOrChangeSignal = [gestureSignal filter:^BOOL(UIGestureRecognizer *gesture) { return gesture.state == UIGestureRecognizerStateChanged || gesture.state == UIGestureRecognizerStateBegan; }], *endOrCancelSignal = [gestureSignal filter:^BOOL(UIGestureRecognizer *gesture) { return gesture.state == UIGestureRecognizerStateEnded || gesture.state == UIGestureRecognizerStateCancelled; }]; RAC(self, contentSnapshotView.center) = [beganOrChangedSignal map:^id(id value) { return [NSValue valueWithCGPoint:[self centerPointForTranslation:[panGesture translationInView:self]]]; }]; [beganOrChangeSignal subscribeNext:^(UIPanGestureRecognizer *panGesture) { [self updateTopViewFrame]; [panGesture setTranslation:CGPointZero inView:self]; }]; [[endOrCancelSignal filter:^BOOL(UIPanGestureRecognizer *gestureRecognizer) { return [self shouldShowRevealView]; }] subscribeNext:^(id x) { [self showRevealViewAnimated:YES]; }]; [[endOrCancelSignal filter:^BOOL(UIPanGestureRecognizer *gestureRecognizer) { return [self shouldHideRevealView]; }] subscribeNext:^(id x) { [self hideRevealViewAnimated:YES]; }]; [[RACSignal merge:@[RACObserve(self, panDirection), RACObserve(self, revealView)]] subscribeNext:^(id x) { [self setNeedsLayout]; }]; [[self rac_prepareForReuseSignal] subscribeNext:^(id x) { [self.revealView removeFromSuperview]; self.revealView = nil; }]; [self addGestureRecognizer:panGesture]; } 
  • Then, I'm exposing a signal property (revealViewSignal) which will send YES/NO values when the reveal view shows/hides. Thus, you can subscribe to this signal and consequently act when the view changes his state. Internally, this signal will be a RACSubject sending next events after each show/hide animation ends:

     - (void)showRevealViewAnimated:(BOOL)animated { [UIView animateWithDuration:animated ? 0.1 : 0.0 animations:^{ // SHOW ANIMATIONS } completion:^(BOOL finished) { [(RACSubject *)self.revealViewSignal sendNext:@(YES)]; }]; } - (void)hideRevealViewAnimated:(BOOL)animated { [UIView animateWithDuration:animated ? 0.1 : 0.0 animations:^{ // HIDE ANIMATIONS } completion:^(BOOL finished) { [(RACSubject *)self.revealViewSignal sendNext:@(NO)]; }]; } 

Everything works as expected but I was just wondering if this is a correct approach to build this kind of view in a RAC way. Also, there are two gesture recognizer delegate methods that I would love to setup in the same setup method above, but I wasn't able to figure out whether it's possible to use the rac_signalForSelector:fromProtocol: method here, so I ended up implementing them as always:

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    return [self checkIfGestureShouldBegin:gestureRecognizer];
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    return [self checkIfGestureShouldRecognize:gestureRecognizer];
}

Any help would be highly appreaciated, thanks!

Unfortunately there's currently no way to use RAC to implement a protocol method that returns a value.

It's a tricky problem since signals aren't required to send values synchronously, but obviously you need to return something when the delegate method is called. You probably don't want to block on the signal because it'd be easy to dead or live lock.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM