Search code examples
iosobjective-csubclassing

Extending PKRevealController with UIScreenEdgePanGestureRecognizer's


I'm trying to use PKRevealController, it comes with UIPanGestureRecognizer on the front view controller so a panning would reveal Left view controller. I wan't to have this panning only when the swipe comes from Edge of the screen. I know I have to use UIScreenEdgePanGestureRecognizer instead of UIPanGestureRecognizer. And I also need the UIPanGestureRecognizer on front view controller when the left view controller is being shown, so that user will be able to swipe the front view controller back.

There is no easy for doing this so I decided to subclass PKRevealController and add new UIScreenEdgePanGestureRecognizer and override some of the methods to this effect. But none of the methods which handle gestures are declared in class extension. So I'm not able to access them in my super class.

Finally decided to make changes in the source code of PKRevealController. I made all the changes and it is working as I wanted. But I know that we should only subclass third party frameworks and extended their functionality and not by changing source code directly. But I'm not finding any work around.

Is there any work around with out making changes in PKRevealController source code itself. Any suggestions would be greatly helpful.


Solution

  • Interestingly, I had the same problem a few weeks ago. I didn't find an easy and completely clean way to solve this, but I did manage to work around by subclassing PKRevealController and being moderately "invasive" (which doesn't mean it can't break with a future update of the lib).

    So, what I did is to create my own UIScreenEdgePanGestureRecognizer and store it in a property (everything here is in my PKRevealController subclass):

    @property (nonatomic, strong) UIScreenEdgePanGestureRecognizer *screenEdgePanGestureRecognizer;
    

    in init:

    // use screen edge pan gesture recognizer instead of normal built-in pan gesture recognizer
    self.recognizesPanningOnFrontView = NO; // disable built-in pan gesture recognizer
    
    self.screenEdgePanGestureRecognizer = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self
                                                                                            action:@selector(didRecognizePanGesture:)];
    self.screenEdgePanGestureRecognizer.maximumNumberOfTouches = 1;
    self.screenEdgePanGestureRecognizer.edges = UIRectEdgeLeft;
    

    Notice, that the action selector (didRecognizePanGesture:) is implemented by the superclass.

    Then, in order to install my own gesture recognizer rather than the built-in one, I've overwritten updatePanGestureRecognizerPresence:

    // overwritten to use our custom screen edge pan gesture recognizer!
    - (void)updatePanGestureRecognizerPresence
    {
        if (![self.frontView.gestureRecognizers containsObject:self.screenEdgePanGestureRecognizer]) {
            [self.frontView addGestureRecognizer:self.screenEdgePanGestureRecognizer];
        }
    }
    

    And since we are accessing some non-publicly declared stuff of PKRevealController (as you have noticed yourself), we need to make it visible (before your @implementation):

    // make private superclass stuff visible to be able to use a screen edge gesture recognizer
    @interface PKRevealController ()
    @property (nonatomic, strong, readwrite) PKRevealControllerView *frontView;
    - (void)didRecognizePanGesture:(UIPanGestureRecognizer *)panGestureRecognizer;
    @end
    

    It works very well and it's the best approach that I could find without having to fork PKRevealController itself. It's definitely a bit fragile, but since I have the full control over when I upgrade PKRevealController that's ok for me.

    The best thing you could do, of course, would be to fork PKRevealController, implement the option to use a screen edge recognizer (or make the whole gesture architecture in there more extensible) and submit a pull request. :-)