Reputation: 305
I want to implement slide menu in my iOS app like drawer (Andriod). I went through a tutorial, but all of them are using third party libraries. Is there any possibility to create a custom slide menu. I tried to create it with the following code, but it's only working with xib file:
- (IBAction)sidemenu:(id)sender
{
[UIView animateWithDuration:0.50f animations:^{
view.frame = self.view.frame;
} completion:^(BOOL finished) {
swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(SwipGestureLeftAction:)];
swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:swipeLeft];
}];
}
- (void)SwipGestureAction
{
UISwipeGestureRecognizer *swiperight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(SwipGestureRightAction:)];
swiperight.direction = UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:swiperight];
}
#pragma mark AddSwipeGestureLeftAndRight
- (void)SwipGestureRightAction:(UISwipeGestureRecognizer *)swipeRight
{
[UIView animateWithDuration:0.50f animations:^{
view.frame = self.view.frame;
} completion:^(BOOL finished) {
swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(SwipGestureLeftAction:)];
swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:swipeLeft];
}];
}
- (void)SwipGestureLeftAction:(UISwipeGestureRecognizer *)swipeRight
{
[UIView animateWithDuration:0.50f animations:^{
[view setFrame:CGRectMake(self.view.frame.origin.x - self.view.frame.size.width, self.view.frame.origin.y, self.view.frame.size.width, self.view.frame.size.height)];
} completion:^(BOOL finished){
[self.view removeGestureRecognizer:swipeLeft];
}];
}
Upvotes: 0
Views: 1332
Reputation: 151
Here is what I have for you:
I made a super class for all my slide menus in all projects. It manages the showing and hiding of the slide menu, and handles orientation changes. It slides in from left on top of the current view, and it partially obscures the remainder of the view with a dark transparent background. If you ever need other behaviour (like pushing out the current view) just override the animation part. My slide menu is a Singleton because in our applications we only use one slide menu on every screen.
#import <UIKit/UIKit.h>
@interface IS_SlideMenu_View : UIView <UIGestureRecognizerDelegate>
{
UIView* transparentBgView;
BOOL hidden;
int lastOrientation;
}
@property (strong, nonatomic) UIView *menuContainerV;
+ (id)sharedInstance;
- (BOOL)isShown;
- (void)hideSlideMenu;
- (void)showSlideMenu;
@end
#import "IS_SlideMenu_View.h"
@implementation IS_SlideMenu_View
+ (id)sharedInstance
{
static id _sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedInstance = [[[self class] alloc] init];
});
return _sharedInstance;
}
- (instancetype)initWithFrame:(CGRect)frame
{
frame = [[[UIApplication sharedApplication] delegate] window].frame;
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor clearColor];
transparentBgView = [[UIView alloc] initWithFrame:frame];
[transparentBgView setBackgroundColor:[UIColor colorWithRed:0 green:0 blue:0 alpha:0.6]];
[transparentBgView setAlpha:0];
transparentBgView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(gestureRecognized:)];
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(gestureRecognized:)];
[transparentBgView addGestureRecognizer:tap];
[transparentBgView addGestureRecognizer:pan];
[self addSubview:transparentBgView];
frame.size.width = 280;
self.menuContainerV = [[UIView alloc] initWithFrame:frame];
CALayer *l = self.menuContainerV.layer;
l.shadowColor = [UIColor blackColor].CGColor;
l.shadowOffset = CGSizeMake(10, 0);
l.shadowOpacity = 1;
l.masksToBounds = NO;
l.shadowRadius = 10;
self.menuContainerV.autoresizingMask = UIViewAutoresizingFlexibleHeight;
[self addSubview: self.menuContainerV];
hidden = YES;
}
//----- SETUP DEVICE ORIENTATION CHANGE NOTIFICATION -----
UIDevice *device = [UIDevice currentDevice];
[device beginGeneratingDeviceOrientationNotifications];
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:@selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:device];
lastOrientation = [[UIDevice currentDevice] orientation];
return self;
}
//********** ORIENTATION CHANGED **********
- (void)orientationChanged:(NSNotification *)note
{
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if(orientation == UIDeviceOrientationPortrait || orientation == UIDeviceOrientationLandscapeLeft || orientation == UIDeviceOrientationLandscapeRight){
NSLog(@"%ld",orientation);
if(!hidden && lastOrientation != orientation){
[self hideSlideMenu];
hidden = YES;
lastOrientation = orientation;
}
}
}
- (void)showSlideMenu {
UIWindow* window = [[[UIApplication sharedApplication] delegate] window];
self.frame = CGRectMake(0, 0, window.frame.size.width, window.frame.size.height);
[self.menuContainerV setTransform:CGAffineTransformMakeTranslation(-window.frame.size.width, 0)];
[window addSubview:self];
// [[UIApplication sharedApplication] setStatusBarHidden:YES];
[UIView animateWithDuration:0.5 animations:^{
[self.menuContainerV setTransform:CGAffineTransformIdentity];
[transparentBgView setAlpha:1];
} completion:^(BOOL finished) {
NSLog(@"Show complete!");
hidden = NO;
}];
}
- (void)gestureRecognized:(UIGestureRecognizer *)recognizer
{
if ([recognizer isKindOfClass:[UITapGestureRecognizer class]]) {
[self hideSlideMenu];
} else if ([recognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
static CGFloat startX;
if (recognizer.state == UIGestureRecognizerStateBegan) {
startX = [recognizer locationInView:self.window].x;
} else
if (recognizer.state == UIGestureRecognizerStateChanged) {
CGFloat touchLocX = [recognizer locationInView:self.window].x;
if (touchLocX < startX) {
[self.menuContainerV setTransform:CGAffineTransformMakeTranslation(touchLocX - startX, 0)];
}
} else
if (recognizer.state == UIGestureRecognizerStateEnded) {
[self hideSlideMenu];
}
}
}
- (void)hideSlideMenu
{
UIWindow* window = [[[UIApplication sharedApplication] delegate] window];
window.backgroundColor = [UIColor clearColor];
[UIView animateWithDuration:0.5 animations:^{
[self.menuContainerV setTransform:CGAffineTransformMakeTranslation(-self.window.frame.size.width, 0)];
[transparentBgView setAlpha:0];
} completion:^(BOOL finished) {
[self removeFromSuperview];
[self.menuContainerV setTransform:CGAffineTransformIdentity];
// [[UIApplication sharedApplication] setStatusBarHidden:NO];
hidden = YES;
NSLog(@"Hide complete!");
}];
}
- (BOOL)isShown
{
return !hidden;
}
@end
Subclasses only need to add subviews to the menuContainerV
view, and manage them.
I created a subclass that has an header view and a table view as its content. I created the content view in a xib, and the owner of the xib is this subclass. This way I can bind outlets to the xib.
#import "IS_SlideMenu_View.h"
@interface CC_SlideMenu_View : IS_SlideMenu_View<UITableViewDelegate, UITableViewDataSource>
@property (weak, nonatomic) IBOutlet UIView *headerView;
@property (weak, nonatomic) IBOutlet UITableView *tableView;
...
@end
When the slide menu gets instantiated I load the xib and add the content view to the menuContainerV view.
#import "CC_SlideMenu_View.h"
@implementation CC_SlideMenu_View
- (instancetype)init
{
self = [super init];
if (self) {
UIView *v = [[[NSBundle mainBundle] loadNibNamed:@"CC_SlideMenu_View" owner:self options:nil] firstObject];
v.frame = self.menuContainerV.bounds;
[self.menuContainerV addSubview:v];
self.tableView.backgroundColor = [UIColor darkGrayColor];
}
return self;
}
...
@end
The result is something like this.
Upvotes: 1