18

I am using SWRevealViewController in order to implement a side nav menu in my app. I would like to make it so that the front view cannot be interacted with when the rear view is opened, except that when the user taps the front view, the rear view will close and the front view can be interacted with again. I have these two SWRevealViewController delegate methods that currently remove interaction from the front view.

- (void)revealController:(SWRevealViewController *)revealController willMoveToPosition:    (FrontViewPosition)position {
    if(position == FrontViewPositionLeft) {
        self.view.userInteractionEnabled = YES;
    } else {
        self.view.userInteractionEnabled = NO;
    }
}

- (void)revealController:(SWRevealViewController *)revealController didMoveToPosition:    (FrontViewPosition)position {
    if(position == FrontViewPositionLeft) {
        self.view.userInteractionEnabled = YES;
    } else {
        self.view.userInteractionEnabled = NO;
    }
}

However this doesn't cause the rear view to close when the front view is tapped. Any help would be greatly appreciated, thanks!

Bartłomiej Semańczyk
  • 59,234
  • 49
  • 233
  • 358
shadowarcher
  • 3,265
  • 5
  • 21
  • 33

9 Answers9

31

If you are using SWIFT, you can do something like this, in your frontViewController:

override func viewDidLoad() {
    super.viewDidLoad()

    if self.revealViewController() != nil {

        self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
        self.view.addGestureRecognizer(self.revealViewController().tapGestureRecognizer())
    }

}

Code works for TAP and PAN gesture.

Adam Studenic
  • 2,115
  • 2
  • 25
  • 22
23

In ViewDidLoad of your frontViewController you need to add a UITapGestureRecognizer

SWRevealViewController *revealController = [self revealViewController];
UITapGestureRecognizer *tap = [revealController tapGestureRecognizer];
tap.delegate = self;

[myView addGestureRecognizer:tap];

This should cause the rear view to close when the front view is tapped which is SWRevealViewController's default behaviour.

Nick
  • 1,315
  • 9
  • 16
  • This is awesome. Add a dummy view to the front if there are buttons to block your dismiss tap. – Tamás Sengel Jul 08 '14 at 08:51
  • 1
    There is a fork of SWRevealViewController for that:http://nsrover.wordpress.com/2014/08/06/swrevealviewcontroller-disabling-user-interaction-when-toggled-to-rearview/ – NSRover Aug 06 '14 at 10:37
  • Can someone please suggest any means that how to override SWRevealViewController's swipe gesture to act over other gestures on the front view eg.Scroll View when user swipes from screen edge? – G.Abhisek Feb 15 '16 at 07:19
12

EDIT: Change UIView's autoresizingMask to suit Swift 2, thanks to Marroc's comment

This is Swift-SWRevealViewController 2.x version of @avdyushin's answer:

func revealController(revealController: SWRevealViewController!, willMoveToPosition position: FrontViewPosition) {
    let tagId = 4207868622

    if revealController.frontViewPosition == FrontViewPosition.Right {
        let lock = self.view.viewWithTag(tagId)
        UIView.animateWithDuration(0.25, animations: {
                lock?.alpha = 0
            }, completion: {(finished: Bool) in
                lock?.removeFromSuperview()
            }
        )
        lock?.removeFromSuperview()
    } else if revealController.frontViewPosition == FrontViewPosition.Left {
        let lock = UIView(frame: self.view.bounds)
        lock.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
        lock.tag = tagId
        lock.alpha = 0
        lock.backgroundColor = UIColor.blackColor()
        lock.addGestureRecognizer(UITapGestureRecognizer(target: self.revealViewController(), action: "revealToggle:"))
        self.view.addSubview(lock)
        UIView.animateWithDuration(0.75, animations: {
                lock.alpha = 0.333
            }
        )
    }
}
mixth
  • 636
  • 5
  • 16
  • If you use Swift 2 change the line of "lock.autoresizingMask" with : lock.autoresizingMask = [UIViewAutoresizing.FlexibleWidth, UIViewAutoresizing.FlexibleHeight] – MarcoCarnevali Dec 09 '15 at 16:19
  • Correct and simple answer with quite a useful logic which can be applied at many places apart from this also. – G.Abhisek Feb 01 '16 at 07:43
  • @mixth . For me why willMoveToPosition is not at all called can u help me why this happens – Uma Madhavi Jun 07 '17 at 06:02
6
  1. Subclass of SWRevealViewController.
  2. Implement revealController:willMoveToPosition: of SWRevealViewControllerDelegate.
  3. Set full screen view on front view controller to override all touches.
  4. Add tap gesture recognizer to hide menu.

Sample with nice animation:

- (void)revealController:(SWRevealViewController *)revealController willMoveToPosition:(FrontViewPosition)position;
{
    static NSInteger tagLockView = 4207868622;
    if (revealController.frontViewPosition == FrontViewPositionRight) {
        UIView *lock = [self.frontViewController.view viewWithTag:tagLockView];
        [UIView animateWithDuration:0.25 animations:^{
            lock.alpha = 0;
        } completion:^(BOOL finished) {
            [lock removeFromSuperview];
        }];
    } else if (revealController.frontViewPosition == FrontViewPositionLeft) {
        UIView *lock = [[UIView alloc] initWithFrame:self.frontViewController.view.bounds];
        lock.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
        lock.tag = tagLockView;
        lock.backgroundColor = [UIColor blackColor];
        lock.alpha = 0;
        [lock addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(revealToggle:)]];
        [self.frontViewController.view addSubview:lock];
        [UIView animateWithDuration:0.75 animations:^{
            lock.alpha = 0.333;
        }];
    }
}

This solution for old version 1.x SWRevealViewController.

avdyushin
  • 1,906
  • 17
  • 21
4

Consider this simple solution, works perfect

private let DimmingViewTag = 10001

extension UIViewController: SWRevealViewControllerDelegate {    

    func setupMenuGestureRecognizer() {

        revealViewController().delegate = self

        view.addGestureRecognizer(revealViewController().panGestureRecognizer())
        view.addGestureRecognizer(revealViewController().tapGestureRecognizer())
    }

    //MARK: - SWRevealViewControllerDelegate

    public func revealController(revealController: SWRevealViewController!, didMoveToPosition position: FrontViewPosition) {

        if case .Right = position {

            let dimmingView = UIView(frame: view.frame)
            dimmingView.tag = DimmingViewTag

            view.addSubview(dimmingView)
            view.bringSubviewToFront(dimmingView)

        } else {
            view.viewWithTag(DimmingViewTag)?.removeFromSuperview()
        }
    }
}

Simple usage in UIViewController:

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)

    setupMenuGestureRecognizer()
}
Bartłomiej Semańczyk
  • 59,234
  • 49
  • 233
  • 358
3

Put below code on menu view controller its works for me

@interface SidebarTableViewController() {
    UIView* coverView;
}

- (void)viewWillDisappear:(BOOL)animated {
    [coverView removeFromSuperview];
    //[self.revealViewController.frontViewController.view setUserInteractionEnabled:YES];
    // get your window screen size
}

- (void)viewWillAppear:(BOOL)animated {
    // get your window screen size
    CGRect screenRect = [[UIScreen mainScreen] bounds];
    //create a new view with the same size
    coverView = [[UIView alloc] initWithFrame:screenRect];
    // change the background color to black and the opacity to 0.6
    coverView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.6];
    // add this new view to your main view
    [self.revealViewController.frontViewController.view addSubview:coverView];
    // [self.revealViewController.frontViewController.view setUserInteractionEnabled:NO];
}
WINSergey
  • 1,977
  • 27
  • 39
abdul sathar
  • 2,395
  • 2
  • 28
  • 38
2

Thanks to mixth & avdyushin for there mighty help. Here's the Swift 4 version derived from there answer to save your couple of hours:

extension UIViewController: SWRevealViewControllerDelegate {

func setupMenuGestureRecognizer() {

    revealViewController().delegate = self

    view.addGestureRecognizer(revealViewController().panGestureRecognizer())
    view.addGestureRecognizer(revealViewController().tapGestureRecognizer())
}

//MARK: - SWRevealViewControllerDelegate
public func revealController(_ revealController: SWRevealViewController!, willMoveTo position: FrontViewPosition) {
    let tagId = 112151
    print("revealController delegate called")
    if revealController.frontViewPosition == FrontViewPosition.right {
        let lock = self.view.viewWithTag(tagId)
        UIView.animate(withDuration: 0.25, animations: {
            lock?.alpha = 0
        }, completion: {(finished: Bool) in
            lock?.removeFromSuperview()
        }
        )
        lock?.removeFromSuperview()
    } else if revealController.frontViewPosition == FrontViewPosition.left {
        let lock = UIView(frame: self.view.bounds)
        lock.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        lock.tag = tagId
        lock.alpha = 0
        lock.backgroundColor = UIColor.black
        lock.addGestureRecognizer(UITapGestureRecognizer(target: self.revealViewController(), action: #selector(SWRevealViewController.revealToggle(_:))))
        self.view.addSubview(lock)
        UIView.animate(withDuration: 0.75, animations: {
            lock.alpha = 0.333
        }
        )
    }
}

}

Now call this function setupMenuGestureRecognizer from viewDidLoad of your rear view controller.

Or you can also implement SWRevealViewControllerDelegate directly to your rear view controller class and use the SWRevealViewControllerDelegate function in the class itself.

Naveen Kumawat
  • 467
  • 5
  • 12
0

I used the tapGestureRecognizer but there are still some problems. I tried this and worked great!

Define class:

@interface IgnoreView : UIView

@property (nonatomic, assign) BOOL shouldAllTouchesBeForMe;

@end

Implement:

@implementation IgnoreView

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    if( self.shouldAllTouchesBeForMe ){
        return self;
    }
    return [super hitTest:point withEvent:event];
}

@end

Then make your View class in Interface Builder of class IgnoreView

In your ViewController, then, do:

in - viewDidLoad

self.revealViewController.delegate = self;
[self.view addGestureRecognizer:self.revealViewController.tapGestureRecognizer];

the implement in your viewcontroller also:

- (void)revealController:(SWRevealViewController *)revealController didMoveToPosition:(FrontViewPosition)position
{
    IgnoreView *i = (id)self.view;
    i.shouldAllTouchesBeForMe = position == FrontViewPositionRight;
}

And you're all set!

noripcord
  • 3,412
  • 5
  • 29
  • 26
-2

On your Menu view controller, add this:

-(void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];

    [self.revealViewController.frontViewController.view setUserInteractionEnabled:NO];
}

-(void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];

    [self.revealViewController.frontViewController.view setUserInteractionEnabled:YES];
}

Then on your Menu Item View Controller, add this on viewDidLoad:

SWRevealViewController *revealController = [self revealViewController];
[revealController tapGestureRecognizer];

Also check out my answer here. It addresses the problem on front view interaction (plus a slide gesture).

Community
  • 1
  • 1
aj_f
  • 932
  • 7
  • 12