├── VERSION
├── Demos
├── Basic
│ ├── BasicDemo
│ │ ├── en.lproj
│ │ │ └── InfoPlist.strings
│ │ ├── Default.png
│ │ ├── Default@2x.png
│ │ ├── Default-568h@2x.png
│ │ ├── View Controllers
│ │ │ ├── MenuViewController.h
│ │ │ ├── DetailsViewController.h
│ │ │ ├── MenuViewController.m
│ │ │ ├── DetailsViewController.m
│ │ │ └── DetailsViewController.xib
│ │ ├── AppDelegate.h
│ │ ├── BasicDemo-Prefix.pch
│ │ ├── main.m
│ │ ├── BasicDemo-Info.plist
│ │ └── AppDelegate.m
│ └── BasicDemo.xcodeproj
│ │ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── BasicDemo.xccheckout
│ │ └── project.pbxproj
└── Demos.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ └── Demos.xccheckout
├── NVSlideMenuController.podspec
├── .gitignore
├── LICENSE.md
├── README.md
├── CHANGELOG.md
└── lib
└── NVSlideMenuController
├── NVSlideMenuController.h
└── NVSlideMenuController.m
/VERSION:
--------------------------------------------------------------------------------
1 | 1.5.8
--------------------------------------------------------------------------------
/Demos/Basic/BasicDemo/en.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /* Localized versions of Info.plist keys */
2 |
3 |
--------------------------------------------------------------------------------
/Demos/Basic/BasicDemo/Default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nverinaud/NVSlideMenuController/HEAD/Demos/Basic/BasicDemo/Default.png
--------------------------------------------------------------------------------
/Demos/Basic/BasicDemo/Default@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nverinaud/NVSlideMenuController/HEAD/Demos/Basic/BasicDemo/Default@2x.png
--------------------------------------------------------------------------------
/Demos/Basic/BasicDemo/Default-568h@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nverinaud/NVSlideMenuController/HEAD/Demos/Basic/BasicDemo/Default-568h@2x.png
--------------------------------------------------------------------------------
/Demos/Demos.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Demos/Basic/BasicDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Demos/Basic/BasicDemo/View Controllers/MenuViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // MenuViewController.h
3 | // BasicDemo
4 | //
5 | // Created by Nicolas Verinaud on 31/12/12.
6 | // Copyright (c) 2012 Nicolas Verinaud. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface MenuViewController : UITableViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/Demos/Basic/BasicDemo/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // BasicDemo
4 | //
5 | // Created by Nicolas Verinaud on 31/12/12.
6 | // Copyright (c) 2012 Nicolas Verinaud. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : UIResponder
12 |
13 | @property (strong, nonatomic) UIWindow *window;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/Demos/Basic/BasicDemo/BasicDemo-Prefix.pch:
--------------------------------------------------------------------------------
1 | //
2 | // Prefix header for all source files of the 'BasicDemo' target in the 'BasicDemo' project
3 | //
4 |
5 | #import
6 |
7 | #ifndef __IPHONE_3_0
8 | #warning "This project uses features only available in iOS SDK 3.0 and later."
9 | #endif
10 |
11 | #ifdef __OBJC__
12 | #import
13 | #import
14 | #endif
15 |
--------------------------------------------------------------------------------
/Demos/Basic/BasicDemo/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // BasicDemo
4 | //
5 | // Created by Nicolas Verinaud on 02/01/13.
6 | // Copyright (c) 2013 Nicolas VERINAUD. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | #import "AppDelegate.h"
12 |
13 | int main(int argc, char *argv[])
14 | {
15 | @autoreleasepool {
16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Demos/Basic/BasicDemo/View Controllers/DetailsViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // DetailsViewController.h
3 | // BasicDemo
4 | //
5 | // Created by Nicolas Verinaud on 31/12/12.
6 | // Copyright (c) 2012 Nicolas Verinaud. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface DetailsViewController : UIViewController
12 |
13 | @property (nonatomic, strong) id detailedObject;
14 |
15 | @property (nonatomic, copy) void(^onShowMenuButtonClicked)(void);
16 | - (void)setOnShowMenuButtonClicked:(void (^)(void))onShowMenuButtonClicked;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/NVSlideMenuController.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = "NVSlideMenuController"
3 | s.version = "1.5.8"
4 | s.summary = "A slide menu done right for iOS."
5 | s.homepage = "https://github.com/nverinaud/NVSlideMenuController"
6 | s.license = 'MIT'
7 | s.author = { "Nicolas VERINAUD" => "n.verinaud@gmail.com" }
8 | s.source = { :git => "https://github.com/nverinaud/NVSlideMenuController.git", :tag => "v1.5.8" }
9 | s.platform = :ios, '5.0'
10 | s.source_files = 'lib', 'lib/**/*.{h,m}'
11 | s.framework = 'QuartzCore'
12 | s.requires_arc = false
13 | end
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ##########
2 | # Xcode
3 | ##########
4 | build/*
5 | *.pbxuser
6 | !default.pbxuser
7 | *.mode1v3
8 | !default.mode1v3
9 | *.mode2v3
10 | !default.mode2v3
11 | *.perspectivev3
12 | !default.perspectivev3
13 | xcuserdata
14 | profile
15 | *.moved-aside
16 |
17 | ##########
18 | # Python
19 | ##########
20 | *.py[co]
21 |
22 | # Packages
23 | *.egg
24 | *.egg-info
25 | dist
26 | build
27 | eggs
28 | parts
29 | bin
30 | var
31 | sdist
32 | develop-eggs
33 | .installed.cfg
34 |
35 | # Installer logs
36 | pip-log.txt
37 |
38 | # Unit test / coverage reports
39 | .coverage
40 | .tox
41 |
42 | #Translations
43 | *.mo
44 |
45 | #Mr Developer
46 | .mr.developer.cfg
47 |
48 | ##########
49 | # OS X
50 | ##########
51 | .DS_Store
52 | Icon?
53 |
54 | # Thumbnails
55 | ._*
56 |
57 | # Files that might appear on external disk
58 | .Spotlight-V100
59 | .Trashes
60 |
61 | ##########
62 | # vim
63 | ##########
64 | .*.sw[a-z]
65 | *.un~
66 | Session.vim
67 |
68 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright 2013 Nicolas VERINAUD
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/Demos/Basic/BasicDemo/BasicDemo-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | ${PRODUCT_NAME}
9 | CFBundleExecutable
10 | ${EXECUTABLE_NAME}
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | ${PRODUCT_NAME}
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1.0
25 | LSRequiresIPhoneOS
26 |
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 | UIInterfaceOrientationPortrait
42 | UIInterfaceOrientationPortraitUpsideDown
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/Demos/Demos.xcworkspace/xcshareddata/Demos.xccheckout:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDESourceControlProjectFavoriteDictionaryKey
6 |
7 | IDESourceControlProjectIdentifier
8 | 29214239-5D7E-448E-894A-BBD9A8C10C38
9 | IDESourceControlProjectName
10 | Demos
11 | IDESourceControlProjectOriginsDictionary
12 |
13 | D4F56436-C0D4-47CE-87FC-332709354658
14 | ssh://github.com/nverinaud/NVSlideMenuController.git
15 |
16 | IDESourceControlProjectPath
17 | Demos/Demos.xcworkspace
18 | IDESourceControlProjectRelativeInstallPathDictionary
19 |
20 | D4F56436-C0D4-47CE-87FC-332709354658
21 | ../..
22 |
23 | IDESourceControlProjectURL
24 | ssh://github.com/nverinaud/NVSlideMenuController.git
25 | IDESourceControlProjectVersion
26 | 110
27 | IDESourceControlProjectWCCIdentifier
28 | D4F56436-C0D4-47CE-87FC-332709354658
29 | IDESourceControlProjectWCConfigurations
30 |
31 |
32 | IDESourceControlRepositoryExtensionIdentifierKey
33 | public.vcs.git
34 | IDESourceControlWCCIdentifierKey
35 | D4F56436-C0D4-47CE-87FC-332709354658
36 | IDESourceControlWCCName
37 | NVSlideMenuController
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/Demos/Basic/BasicDemo.xcodeproj/project.xcworkspace/xcshareddata/BasicDemo.xccheckout:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDESourceControlProjectFavoriteDictionaryKey
6 |
7 | IDESourceControlProjectIdentifier
8 | 34F85F4D-AD26-4514-947C-0FF3FE220181
9 | IDESourceControlProjectName
10 | BasicDemo
11 | IDESourceControlProjectOriginsDictionary
12 |
13 | 58C815CD-FAFD-482E-B11F-4C9EAFF3E6F8
14 | ssh://github.com/nverinaud/NVSlideMenuController.git
15 |
16 | IDESourceControlProjectPath
17 | Demos/Basic/BasicDemo.xcodeproj/project.xcworkspace
18 | IDESourceControlProjectRelativeInstallPathDictionary
19 |
20 | 58C815CD-FAFD-482E-B11F-4C9EAFF3E6F8
21 | ../../../..
22 |
23 | IDESourceControlProjectURL
24 | ssh://github.com/nverinaud/NVSlideMenuController.git
25 | IDESourceControlProjectVersion
26 | 110
27 | IDESourceControlProjectWCCIdentifier
28 | 58C815CD-FAFD-482E-B11F-4C9EAFF3E6F8
29 | IDESourceControlProjectWCConfigurations
30 |
31 |
32 | IDESourceControlRepositoryExtensionIdentifierKey
33 | public.vcs.git
34 | IDESourceControlWCCIdentifierKey
35 | 58C815CD-FAFD-482E-B11F-4C9EAFF3E6F8
36 | IDESourceControlWCCName
37 | NVSlideMenuController
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/Demos/Basic/BasicDemo/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // BasicDemo
4 | //
5 | // Created by Nicolas Verinaud on 31/12/12.
6 | // Copyright (c) 2012 Nicolas Verinaud. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 | #import "NVSlideMenuController.h"
11 | #import "MenuViewController.h"
12 | #import "DetailsViewController.h"
13 |
14 | void uncaughtExceptionHandler(NSException*);
15 |
16 | @implementation AppDelegate
17 |
18 | #if !__has_feature(objc_arc)
19 | - (void)dealloc
20 | {
21 | [_window release];
22 |
23 | [super dealloc];
24 | }
25 | #endif
26 |
27 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
28 | {
29 | NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);
30 |
31 | #if !__has_feature(objc_arc)
32 | self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
33 | #else
34 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
35 | #endif
36 |
37 | MenuViewController *menuVC = [[MenuViewController alloc] initWithStyle:UITableViewStyleGrouped];
38 | UINavigationController *menuNavigationController = [[UINavigationController alloc] initWithRootViewController:menuVC];
39 |
40 | DetailsViewController *detailsVC = [[DetailsViewController alloc] init];
41 | detailsVC.detailedObject = @"Welcome Slide Menu !";
42 | UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:detailsVC];
43 |
44 | NVSlideMenuController *slideMenuVC = [[NVSlideMenuController alloc] initWithMenuViewController:menuNavigationController andContentViewController:navController];
45 |
46 | self.window.rootViewController = slideMenuVC;
47 |
48 | #if !__has_feature(objc_arc)
49 | [menuVC release];
50 | [detailsVC release];
51 | [menuNavigationController release];
52 | [navController release];
53 | [slideMenuVC release];
54 | #endif
55 |
56 | self.window.backgroundColor = [UIColor whiteColor];
57 | [self.window makeKeyAndVisible];
58 | return YES;
59 | }
60 |
61 | @end
62 |
63 | void uncaughtExceptionHandler(NSException *exception)
64 | {
65 | NSLog(@"CRASH: %@", exception);
66 | NSLog(@"Stack Trace: %@", [exception callStackSymbols]);
67 | }
68 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # NVSlideMenuController
2 |
3 | A slide menu done right.
4 |
5 | ## Requirements
6 |
7 | * You can use ARC or not, this library supports both
8 | * iOS 5.0+
9 |
10 | ## Usage
11 |
12 | * Drop `lib/NVSlideMenuController/NVSlideMenuController.{h|m}` in your project
13 | * Add `QuartzCore.framework`
14 |
15 | **Create a slide menu**
16 |
17 | ```objective-c
18 | UIViewController *menuViewController = ... ; // Your menu view controller
19 | UIViewController *contentViewController = ... ; // The initial content view controller (home page ?)
20 |
21 | NVSlideMenuController *slideMenuController = [[NVSlideMenuController alloc] initWithMenuViewController:menuViewController andContentViewController:contentViewController];
22 |
23 | self.window.rootViewController = slideMenuController; // Assuming you are in app delegate did finish launching
24 | ```
25 |
26 | **Change & show new content from the menu**
27 |
28 | ```objective-c
29 | // Inside your menuViewController
30 | UIViewController *newContentViewController = ... ; // Create & configure your new content view controller (as usual)
31 | [self.slideMenuController closeMenuBehindContentViewController:newContentViewController animated:YES completion:nil];
32 | ```
33 |
34 | **NVSlideMenuController callbacks**
35 |
36 | The library provides 4 methods through a UIViewController category.
37 | You can override them to manage the slide in/out of the content view controller. It is best described by the provided demo app.
38 |
39 | ```objective-c
40 | @interface UIViewController (NVSlideMenuControllerCallbacks)
41 |
42 | - (void)viewWillSlideIn:(BOOL)animated inSlideMenuController:(NVSlideMenuController *)slideMenuController;
43 | - (void)viewDidSlideIn:(BOOL)animated inSlideMenuController:(NVSlideMenuController *)slideMenuController;
44 | - (void)viewWillSlideOut:(BOOL)animated inSlideMenuController:(NVSlideMenuController *)slideMenuController;
45 | - (void)viewDidSlideOut:(BOOL)animated inSlideMenuController:(NVSlideMenuController *)slideMenuController;
46 |
47 | @end
48 | ```
49 |
50 | **Change the slide direction**
51 |
52 | You can specify the slide direction by setting the `slideDirection` property to `NVSlideMenuControllerSlideFromLeftToRight`
53 | or `NVSlideMenuControllerSlideFromRightToLeft`. The views will update accordingly if needed (look at the demo app for a taste).
54 | You can also animate the change.
55 |
56 | ```objective-c
57 | // Inside your view controller (menu or content)
58 | [self.slideMenuController setSlideDirection:NVSlideMenuControllerSlideFromRightToLeft animated:YES];
59 | // or more simply...
60 | self.slideMenuController.slideDirection = NVSlideMenuControllerSlideFromRightToLeft; // this one will not animate
61 | ```
62 |
63 | **Enable/Disable the pan gesture**
64 |
65 | You could need to disable the pan gesture, for example when your content view controller has a table view with reorder control (see issue [#2](https://github.com/nverinaud/NVSlideMenuController/issues/2)).
66 |
67 | ```objective-c
68 | // For example when your view controller enter in editing mode
69 | - (void)setEditing:(BOOL)editing animated:(BOOL)animated
70 | {
71 | [super setEditing:editing animated:animated];
72 |
73 | if (editing)
74 | self.slideMenuController.panGestureEnabled = NO;
75 | }
76 | ```
77 |
78 | For more have a look at the demo app `;-]`
79 |
80 | ## Author
81 |
82 | Nicolas VERINAUD ([@nverinaud](https://twitter.com/nverinaud))
83 |
84 | ## License
85 |
86 | Released under the MIT License. For more see `LICENSE.md`.
87 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## Release 1.5.7
4 |
5 | * FIX: issue [#33](https://github.com/nverinaud/NVSlideMenuController/issues/33) has been fixed. Closing the menu while the menu view controller's view is not loaded does not cause it to load in an inconsistent manner.
6 |
7 | ## Release 1.5.6
8 |
9 | * FIX: forward status bar style & appearance to child view controllers.
10 |
11 | ## Release 1.5.5
12 |
13 | * FIX: fix the toggle of the content view.
14 |
15 | ## Release 1.5.4
16 |
17 | * FIX: fix implementation of custom container view controller. (Thanks to [Gabriel Reis](https://github.com/greis))
18 |
19 | ## Release 1.5.3
20 |
21 | * FIX: rotation issue introduced in 1.5.2 (sorry about that!)
22 | * FIX: rotation issue on panning (issue #8)
23 |
24 | ## Release 1.5.2
25 |
26 | * NEW: You can now enable/disable the shadow on the content view. (Thanks to [Gabriel Reis & Matt Polito from Hashrocket](https://github.com/hashrocketeer))
27 | * NEW: add `menuWidth` property, it replaces `contentViewWidthWhenMenuIsOpen`
28 | * DEPRECATION: `contentViewWidthWhenMenuIsOpen` is now deprecated
29 |
30 | ## Release 1.5.1
31 |
32 | * FIX: fix initial bounds of content view controller in viewDidLoad.
33 |
34 | ## Release 1.5.0
35 |
36 | * ENHANCEMENT: Remove the panGesture's requirement that the tapGesture fails. (Thanks to [David Berry](https://github.com/DavidBarry))
37 | * NEW: You can now make the content view bounce when navigating. (Thanks to [David Berry](https://github.com/DavidBarry))
38 |
39 | ## Release 1.4.3
40 |
41 | * FIX: issue [#5](https://github.com/nverinaud/NVSlideMenuController/issues/5) has been fixed.
42 |
43 | ## Release 1.4.2
44 |
45 | * FIX: fix bridge cast warning when not using ARC.
46 |
47 | ## Release 1.4.1
48 |
49 | * FIX: fix performance issue due to the shadow.
50 |
51 | ## Release 1.4.0
52 |
53 | * NEW: You can now completly hide the content view and show it partially.
54 | * DEPRECATED APIs:
55 | * `-setContentViewController:animated:completion:` replaced by `-closeMenuBehindContentViewController:animated:completion:`
56 | * `-showContentViewControllerAnimated:completion:` replaced by `-closeMenuAnimated:completion:`
57 | * `-showMenuAnimated:completion:` replaced by `-openMenuAnimated:completion:`.
58 |
59 | ## Release 1.3.2
60 |
61 | * NEW: add `contentViewWidthWhenMenuIsOpen` property.
62 |
63 | ## Release 1.3.1
64 |
65 | * IMPORTANT FIX: the pan gesture now works fine.
66 | * NEW: ARC is supported conditionaly.
67 |
68 | ## Release 1.3.0
69 |
70 | * NEW: You can now change the slide direction (left -> right or right -> left).
71 |
72 | ## Release 1.2.0
73 |
74 | * NEW: add `NVSlideMenuControllerCallbacks` category on `UIViewController`.
75 | * NEW: default implementation of `NVSlideMenuControllerCallbacks` category has
76 | been added to View Controller Containers provided by Apple.
77 |
78 | ## Release 1.1.3
79 |
80 | * NEW: add `panGestureEnabled` property.
81 | * DEPRECATED: `panEnabledWhenSlideMenuIsHidden` property is now deprecated.
82 | * FIX: issue [#2](https://github.com/nverinaud/NVSlideMenuController/issues/2) has been fixed.
83 |
84 | ## Release 1.1.2
85 |
86 | * UPDATE: make `-[NVSlideMenuController isMenuOpen]` a public API.
87 |
88 | ## Release 1.1.1
89 |
90 | * FIX: fix a typo in README for `no ARC compiler flag`.
91 |
92 | ## Release 1.0.2
93 |
94 | * NEW: add a nice shadow on the content view controller's view.
95 |
96 | ## Release 1.0.1
97 |
98 | * FIX: The menu state is now preserved when presenting a view controller modally.
99 |
100 | ## Release 1.0.0
101 |
102 | * First release.
103 |
--------------------------------------------------------------------------------
/lib/NVSlideMenuController/NVSlideMenuController.h:
--------------------------------------------------------------------------------
1 | //
2 | // NVSlideMenuViewController.h
3 | // NVSlideMenuViewController
4 | //
5 | // Created by Nicolas Verinaud on 31/12/12.
6 | // Copyright (c) 2012 Nicolas Verinaud. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | typedef NS_ENUM(NSUInteger, NVSlideMenuControllerSlideDirection)
12 | {
13 | NVSlideMenuControllerSlideFromLeftToRight = 0, // default, slide from left to right to open the menu
14 | NVSlideMenuControllerSlideFromRightToLeft // slide from right to left to open the menu
15 | };
16 |
17 |
18 | @interface NVSlideMenuController : UIViewController
19 |
20 | @property (nonatomic, readonly, strong) UIViewController *menuViewController;
21 | @property (nonatomic, readonly, strong) UIViewController *contentViewController;
22 | @property (nonatomic, assign) BOOL panGestureEnabled; // default is YES. Set it to NO to disable the pan gesture
23 | @property (nonatomic, assign) CGFloat menuWidth; // default is 276
24 | @property (nonatomic, assign) BOOL autoAdjustMenuWidth; // default is YES. Set it to NO to keep the menu the same width as the SlideMenuController's view
25 | @property (nonatomic, assign) BOOL bounceWhenNavigating; // default is NO. Determines whether the contentViewController will bounce offscreen when calling
26 | // `-closeMenuBehindContentViewController:animated:completion:`
27 | @property (nonatomic, assign) BOOL showShadowOnContentView; // default is YES. Set it to NO to remove shadow from content view
28 |
29 | - (id)initWithMenuViewController:(UIViewController *)menuViewController andContentViewController:(UIViewController *)contentViewController;
30 |
31 | /** @name Navigation */
32 | - (IBAction)toggleMenuAnimated:(id)sender; // Convenience for use with target/action, always animate
33 |
34 | - (void)openMenuAnimated:(BOOL)animated completion:(void(^)(BOOL finished))completion;
35 | - (void)closeMenuAnimated:(BOOL)animated completion:(void(^)(BOOL finished))completion;
36 | - (void)closeMenuBehindContentViewController:(UIViewController *)contentViewController animated:(BOOL)animated completion:(void(^)(BOOL finished))completion;
37 | - (void)closeMenuBehindContentViewController:(UIViewController *)contentViewController animated:(BOOL)animated bounce:(BOOL)bounce completion:(void(^)(BOOL finished))completion;
38 |
39 | - (void)hideContentViewControllerAnimated:(BOOL)animated completion:(void(^)(BOOL finished))completion; // hide the content view controller, the menu view controller will be resized
40 | - (void)partiallyShowContentViewControllerAnimated:(BOOL)animated completion:(void(^)(BOOL finished))completion; // show a part (equal to contentViewWidthWhenMenuIsOpen) of the content view controller, the menu view controller will be resized
41 |
42 | /** @name Slide direction */
43 | @property (nonatomic, assign) NVSlideMenuControllerSlideDirection slideDirection;
44 | - (void)setSlideDirection:(NVSlideMenuControllerSlideDirection)slideDirection animated:(BOOL)animated;
45 |
46 | /** @name Menu state information */
47 | - (BOOL)isMenuOpen;
48 | - (BOOL)isContentViewHidden;
49 |
50 | @end
51 |
52 |
53 | #pragma mark - UIViewController (NVSlideMenuController)
54 |
55 | @interface UIViewController (NVSlideMenuController)
56 |
57 | @property (nonatomic, readonly) NVSlideMenuController *slideMenuController;
58 |
59 | @end
60 |
61 |
62 | #pragma mark - UIViewController (NVSlideMenuControllerCallbacks)
63 |
64 | /**
65 | Subclasses may override these methods to perform custom actions (such as disable interaction with a web view or a table view)
66 | when they slide in our out.
67 | These callbacks are only called on the contentViewController of the slideMenuController.
68 | */
69 | @interface UIViewController (NVSlideMenuControllerCallbacks)
70 |
71 | - (void)viewWillSlideIn:(BOOL)animated inSlideMenuController:(NVSlideMenuController *)slideMenuController; // default implementation does nothing
72 | - (void)viewDidSlideIn:(BOOL)animated inSlideMenuController:(NVSlideMenuController *)slideMenuController; // default implementation does nothing
73 | - (void)viewWillSlideOut:(BOOL)animated inSlideMenuController:(NVSlideMenuController *)slideMenuController; // default implementation does nothing
74 | - (void)viewDidSlideOut:(BOOL)animated inSlideMenuController:(NVSlideMenuController *)slideMenuController; // default implementation does nothing
75 |
76 | @end
77 |
78 |
79 | #pragma mark - NVSlideMenuController (Deprecated)
80 |
81 | @interface NVSlideMenuController (Deprecated)
82 |
83 | @property (nonatomic, assign) CGFloat contentViewWidthWhenMenuIsOpen DEPRECATED_ATTRIBUTE; // default is 44.0, DEPRECATED use `menuWidth` property instead
84 |
85 | - (void)setContentViewController:(UIViewController *)contentViewController animated:(BOOL)animated completion:(void(^)(BOOL finished))completion DEPRECATED_ATTRIBUTE; // Use `-closeMenuBehindContentViewController:animated:completion:` instead
86 | - (void)showContentViewControllerAnimated:(BOOL)animated completion:(void(^)(BOOL finished))completion DEPRECATED_ATTRIBUTE; // Use `-closeMenuAnimated:completion:` instead
87 | - (void)showMenuAnimated:(BOOL)animated completion:(void(^)(BOOL finished))completion DEPRECATED_ATTRIBUTE; // Use `-openMenuAnimated:completion:` instead
88 |
89 | @property (nonatomic, assign) BOOL panEnabledWhenSlideMenuIsHidden DEPRECATED_ATTRIBUTE; // Use `panGestureEnabled` property to control whether the pan gesture is enabled.
90 |
91 | @end
92 |
--------------------------------------------------------------------------------
/Demos/Basic/BasicDemo/View Controllers/MenuViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // MenuViewController.m
3 | // BasicDemo
4 | //
5 | // Created by Nicolas Verinaud on 31/12/12.
6 | // Copyright (c) 2012 Nicolas Verinaud. All rights reserved.
7 | //
8 |
9 | #import "MenuViewController.h"
10 | #import "DetailsViewController.h"
11 | #import "NVSlideMenuController.h"
12 |
13 | #define NUMBER_OF_SECTIONS 3
14 | #define NUMBER_OF_ROWS 10
15 |
16 | @interface MenuViewController ()
17 |
18 | - (void)togglePanGestureEnabled:(UIBarButtonItem *)sender;
19 | - (void)toggleSlideDirection:(UIBarButtonItem *)sender;
20 |
21 | @end
22 |
23 |
24 |
25 | @implementation MenuViewController
26 |
27 | - (id)initWithStyle:(UITableViewStyle)style
28 | {
29 | self = [super initWithStyle:style];
30 | return self;
31 | }
32 |
33 |
34 | #pragma mark - View lifecycle
35 |
36 | - (void)viewDidLoad
37 | {
38 | [super viewDidLoad];
39 |
40 | self.clearsSelectionOnViewWillAppear = NO;
41 | self.title = NSLocalizedString(@"Menu", nil);
42 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Pan Enabled", nil)
43 | style:UIBarButtonItemStyleBordered
44 | target:self
45 | action:@selector(togglePanGestureEnabled:)];
46 |
47 | self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"L->R", nil)
48 | style:UIBarButtonItemStyleBordered
49 | target:self
50 | action:@selector(toggleSlideDirection:)];
51 |
52 | NSLog(@"%@ - %@ - View Frame: %@", self, NSStringFromSelector(_cmd), NSStringFromCGRect(self.view.frame));
53 | }
54 |
55 |
56 | - (void)viewWillAppear:(BOOL)animated
57 | {
58 | [super viewWillAppear:animated];
59 |
60 | NSLog(@"%@ - %@ - View Frame: %@", self, NSStringFromSelector(_cmd), NSStringFromCGRect(self.view.frame));
61 | }
62 |
63 |
64 | - (void)viewDidAppear:(BOOL)animated
65 | {
66 | [super viewDidAppear:animated];
67 |
68 | NSLog(@"%@ - %@ - View Frame: %@", self, NSStringFromSelector(_cmd), NSStringFromCGRect(self.view.frame));
69 |
70 | [self.tableView scrollToNearestSelectedRowAtScrollPosition:UITableViewScrollPositionNone animated:animated];
71 | }
72 |
73 |
74 | - (void)viewWillDisappear:(BOOL)animated
75 | {
76 | [super viewWillDisappear:animated];
77 |
78 | NSLog(@"%@ - %@ - View Frame: %@", self, NSStringFromSelector(_cmd), NSStringFromCGRect(self.view.frame));
79 | }
80 |
81 |
82 | - (void)viewDidDisappear:(BOOL)animated
83 | {
84 | [super viewDidDisappear:animated];
85 |
86 | NSLog(@"%@ - %@ - View Frame: %@", self, NSStringFromSelector(_cmd), NSStringFromCGRect(self.view.frame));
87 | }
88 |
89 | #pragma mark - Table view data source
90 |
91 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
92 | {
93 | return NUMBER_OF_SECTIONS;
94 | }
95 |
96 |
97 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
98 | {
99 | return NUMBER_OF_ROWS;
100 | }
101 |
102 |
103 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
104 | {
105 | static NSString *CellIdentifier = @"Cell";
106 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
107 | if (!cell)
108 | {
109 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
110 | #if !__has_feature(objc_arc)
111 | [cell autorelease];
112 | #endif
113 | }
114 |
115 | // First row of first section toggle menu full screen
116 | if (indexPath.section == 0 && indexPath.row == 0)
117 | cell.textLabel.text = NSLocalizedString(@"Toggle Content View", nil);
118 | // Second row of first section shows a modal view controller
119 | else if (indexPath.section == 0 && indexPath.row == 1)
120 | cell.textLabel.text = NSLocalizedString(@"Show modal", nil);
121 | else
122 | cell.textLabel.text = [NSString stringWithFormat:@"Section: %ld - Row: %ld", (long)indexPath.section, (long)indexPath.row];
123 |
124 | return cell;
125 | }
126 |
127 |
128 | #pragma mark - Table view delegate
129 |
130 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
131 | {
132 | if (self.slideMenuController)
133 | {
134 | DetailsViewController *detailsVC = [DetailsViewController new];
135 | detailsVC.detailedObject = indexPath;
136 |
137 | if (indexPath.section == 0 && indexPath.row == 0)
138 | {
139 | if ([self.slideMenuController isContentViewHidden])
140 | [self.slideMenuController partiallyShowContentViewControllerAnimated:YES completion:nil];
141 | else
142 | [self.slideMenuController hideContentViewControllerAnimated:YES completion:nil];
143 |
144 | [tableView deselectRowAtIndexPath:indexPath animated:YES];
145 | }
146 | else if (indexPath.section == 0 && indexPath.row == 1)
147 | {
148 | [detailsVC setOnShowMenuButtonClicked:^{
149 | [self dismissViewControllerAnimated:YES completion:nil];
150 | }];
151 | [self presentViewController:detailsVC animated:YES completion:nil];
152 | }
153 | else
154 | {
155 | UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:detailsVC];
156 | [self.slideMenuController closeMenuBehindContentViewController:navController animated:YES completion:nil];
157 | #if !__has_feature(objc_arc)
158 | [navController release];
159 | #endif
160 | }
161 |
162 | #if !__has_feature(objc_arc)
163 | [detailsVC release];
164 | #endif
165 | }
166 | else
167 | {
168 | [tableView deselectRowAtIndexPath:indexPath animated:YES];
169 | }
170 | }
171 |
172 |
173 | #pragma mark - Rotation
174 |
175 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
176 | {
177 | return toInterfaceOrientation != UIInterfaceOrientationPortraitUpsideDown;
178 | }
179 |
180 |
181 | #pragma mark - Toggle pan gesture enabled
182 |
183 | - (void)togglePanGestureEnabled:(UIBarButtonItem *)sender
184 | {
185 | self.slideMenuController.panGestureEnabled = !self.slideMenuController.panGestureEnabled;
186 | sender.title = self.slideMenuController.panGestureEnabled ? NSLocalizedString(@"Pan Enabled", nil) : NSLocalizedString(@"Pan Disabled", nil);
187 | }
188 |
189 |
190 | #pragma mark - Toggle slide direction
191 |
192 | - (void)toggleSlideDirection:(UIBarButtonItem *)sender
193 | {
194 | if (self.slideMenuController.slideDirection == NVSlideMenuControllerSlideFromLeftToRight)
195 | {
196 | [self.slideMenuController setSlideDirection:NVSlideMenuControllerSlideFromRightToLeft animated:YES];
197 | sender.title = NSLocalizedString(@"R->L", nil);
198 | }
199 | else if (self.slideMenuController.slideDirection == NVSlideMenuControllerSlideFromRightToLeft)
200 | {
201 | [self.slideMenuController setSlideDirection:NVSlideMenuControllerSlideFromLeftToRight animated:YES];
202 | sender.title = NSLocalizedString(@"L->R", nil);
203 | }
204 | }
205 |
206 |
207 | @end
208 |
--------------------------------------------------------------------------------
/Demos/Basic/BasicDemo/View Controllers/DetailsViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // DetailsViewController.m
3 | // BasicDemo
4 | //
5 | // Created by Nicolas Verinaud on 31/12/12.
6 | // Copyright (c) 2012 Nicolas Verinaud. All rights reserved.
7 | //
8 |
9 | #import "DetailsViewController.h"
10 | #import "NVSlideMenuController.h"
11 |
12 | @interface DetailsViewController ()
13 |
14 | @property (strong, nonatomic) IBOutlet UITextView *textView;
15 | @property (strong, nonatomic) IBOutlet UISwitch *bounceSwitch;
16 | @property (strong, nonatomic) IBOutlet UISwitch *menuWidthSwitch;
17 | @property (strong, nonatomic) IBOutlet UIToolbar *toolbar;
18 | - (void)updateTextViewAccordingToSlideMenuControllerDirection;
19 |
20 | // Lazy buttons
21 | @property (strong, nonatomic) UIBarButtonItem *leftBarButtonItem;
22 | @property (strong, nonatomic) UIBarButtonItem *rightBarButtonItem;
23 | - (void)updateBarButtonsAccordingToSlideMenuControllerDirectionAnimated:(BOOL)animated;
24 |
25 | // Actions
26 | - (IBAction)showMenu:(id)sender;
27 | - (IBAction)changeSlideDirection:(id)sender;
28 | - (IBAction)bounceSwitchValueChanged:(id)sender;
29 | - (IBAction)menuWidthSwitchValueChanged:(id)sender;
30 | - (IBAction)hideKeyboard:(id)sender;
31 |
32 | @end
33 |
34 | @implementation DetailsViewController
35 |
36 | #if !__has_feature(objc_arc)
37 | - (void)dealloc
38 | {
39 | NSLog(@"%p dealloc'ed", self);
40 |
41 | [_detailedObject release];
42 | [_onShowMenuButtonClicked release];
43 |
44 | [_textView release];
45 | [_bounceSwitch release];
46 | [_menuWidthSwitch release];
47 | [_toolbar release];
48 |
49 | [_leftBarButtonItem release];
50 | [_rightBarButtonItem release];
51 |
52 | [super dealloc];
53 | }
54 | #endif
55 |
56 |
57 | #pragma mark - Creation
58 | #pragma mark - Overriden Designated Initializer
59 |
60 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
61 | {
62 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
63 | if (self)
64 | {
65 | if ([self respondsToSelector:@selector(edgesForExtendedLayout)])
66 | {
67 | self.edgesForExtendedLayout = UIRectEdgeNone;
68 | }
69 | }
70 |
71 | return self;
72 | }
73 |
74 |
75 | #pragma mark - Memory Management
76 |
77 | - (void)didReceiveMemoryWarning
78 | {
79 | [super didReceiveMemoryWarning];
80 |
81 | if (![self isViewLoaded])
82 | {
83 | self.textView = nil;
84 | self.toolbar = nil;
85 | }
86 | }
87 |
88 |
89 | - (void)viewDidUnload
90 | {
91 | [self setTextView:nil];
92 | [self setBounceSwitch:nil];
93 | [self setMenuWidthSwitch:nil];
94 | self.toolbar = nil;
95 | [super viewDidUnload];
96 | }
97 |
98 |
99 | #pragma mark - View lifecycle
100 |
101 | - (void)viewDidLoad
102 | {
103 | [super viewDidLoad];
104 |
105 | NSLog(@"%@ - %@ - View Frame: %@", self, NSStringFromSelector(_cmd), NSStringFromCGRect(self.view.frame));
106 |
107 | self.title = NSLocalizedString(@"Details", nil);
108 |
109 | [self updateTextViewAccordingToSlideMenuControllerDirection];
110 | [self updateBarButtonsAccordingToSlideMenuControllerDirectionAnimated:NO];
111 |
112 | self.bounceSwitch.on = self.slideMenuController.bounceWhenNavigating;
113 | self.menuWidthSwitch.on = self.slideMenuController.autoAdjustMenuWidth;
114 | self.textView.inputAccessoryView = self.toolbar;
115 | }
116 |
117 |
118 | - (void)updateTextViewAccordingToSlideMenuControllerDirection
119 | {
120 | NSString *slideDirectionAsString = nil;
121 | if (self.slideMenuController.slideDirection == NVSlideMenuControllerSlideFromLeftToRight)
122 | slideDirectionAsString = @"From left to right";
123 | else if (self.slideMenuController.slideDirection == NVSlideMenuControllerSlideFromRightToLeft)
124 | slideDirectionAsString = @"From right to left";
125 |
126 | self.textView.text = [NSString stringWithFormat:@"Direction: %@", slideDirectionAsString];
127 | }
128 |
129 |
130 | - (void)viewWillAppear:(BOOL)animated
131 | {
132 | [super viewWillAppear:animated];
133 |
134 | NSLog(@"%@ - %@ - View Frame: %@", self, NSStringFromSelector(_cmd), NSStringFromCGRect(self.view.frame));
135 | }
136 |
137 |
138 | - (void)viewDidAppear:(BOOL)animated
139 | {
140 | [super viewDidAppear:animated];
141 |
142 | NSLog(@"%@ - %@ - View Frame: %@", self, NSStringFromSelector(_cmd), NSStringFromCGRect(self.view.frame));
143 | }
144 |
145 |
146 | - (void)viewWillDisappear:(BOOL)animated
147 | {
148 | [super viewWillDisappear:animated];
149 |
150 | NSLog(@"%@ - %@ - View Frame: %@", self, NSStringFromSelector(_cmd), NSStringFromCGRect(self.view.frame));
151 | }
152 |
153 |
154 | - (void)viewDidDisappear:(BOOL)animated
155 | {
156 | [super viewDidDisappear:animated];
157 |
158 | NSLog(@"%@ - %@ - View Frame: %@", self, NSStringFromSelector(_cmd), NSStringFromCGRect(self.view.frame));
159 | }
160 |
161 |
162 | #pragma mark - Slide callbacks
163 |
164 | /**
165 | Here is an example of how you can best use these callbacks !
166 | */
167 | - (void)viewWillSlideIn:(BOOL)animated inSlideMenuController:(NVSlideMenuController *)slideMenuController
168 | {
169 | NSLog(@"%@ - %@ - View Frame: %@", self, NSStringFromSelector(_cmd), NSStringFromCGRect(self.view.frame));
170 |
171 | [self.textView setEditable:YES];
172 | [self.textView setScrollEnabled:YES];
173 | }
174 |
175 |
176 | - (void)viewDidSlideIn:(BOOL)animated inSlideMenuController:(NVSlideMenuController *)slideMenuController
177 | {
178 | NSLog(@"%@ - %@ - View Frame: %@", self, NSStringFromSelector(_cmd), NSStringFromCGRect(self.view.frame));
179 |
180 | [self.textView becomeFirstResponder];
181 | }
182 |
183 |
184 | - (void)viewWillSlideOut:(BOOL)animated inSlideMenuController:(NVSlideMenuController *)slideMenuController
185 | {
186 | NSLog(@"%@ - %@ - View Frame: %@", self, NSStringFromSelector(_cmd), NSStringFromCGRect(self.view.frame));
187 |
188 | [self.textView resignFirstResponder];
189 | }
190 |
191 |
192 | - (void)viewDidSlideOut:(BOOL)animated inSlideMenuController:(NVSlideMenuController *)slideMenuController
193 | {
194 | NSLog(@"%@ - %@ - View Frame: %@", self, NSStringFromSelector(_cmd), NSStringFromCGRect(self.view.frame));
195 |
196 | [self.textView setEditable:NO];
197 | [self.textView setScrollEnabled:NO];
198 | }
199 |
200 |
201 | #pragma mark - Show menu
202 |
203 | - (IBAction)showMenu:(id)sender
204 | {
205 | if (self.onShowMenuButtonClicked)
206 | self.onShowMenuButtonClicked();
207 | else
208 | [self.slideMenuController openMenuAnimated:YES completion:nil];
209 | }
210 |
211 |
212 | #pragma mark - Change slide direction
213 |
214 | - (IBAction)changeSlideDirection:(id)sender
215 | {
216 | if (self.slideMenuController.slideDirection == NVSlideMenuControllerSlideFromLeftToRight)
217 | self.slideMenuController.slideDirection = NVSlideMenuControllerSlideFromRightToLeft;
218 | else if (self.slideMenuController.slideDirection == NVSlideMenuControllerSlideFromRightToLeft)
219 | self.slideMenuController.slideDirection = NVSlideMenuControllerSlideFromLeftToRight;
220 |
221 | [self updateBarButtonsAccordingToSlideMenuControllerDirectionAnimated:YES];
222 | [self updateTextViewAccordingToSlideMenuControllerDirection];
223 | }
224 |
225 |
226 | - (IBAction)bounceSwitchValueChanged:(id)sender
227 | {
228 | UISwitch *theSwitch = sender;
229 | self.slideMenuController.bounceWhenNavigating = theSwitch.isOn;
230 | }
231 |
232 |
233 | - (IBAction)menuWidthSwitchValueChanged:(id)sender
234 | {
235 | UISwitch *theSwitch = sender;
236 | self.slideMenuController.autoAdjustMenuWidth = theSwitch.isOn;
237 | }
238 |
239 |
240 | - (IBAction)hideKeyboard:(id)sender
241 | {
242 | [self.view endEditing:YES];
243 | }
244 |
245 |
246 | #pragma mark - Lazy buttons
247 |
248 | - (UIBarButtonItem *)leftBarButtonItem
249 | {
250 | if (!_leftBarButtonItem)
251 | {
252 | _leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFastForward
253 | target:self.slideMenuController
254 | action:@selector(toggleMenuAnimated:)];
255 | }
256 | return _leftBarButtonItem;
257 | }
258 |
259 |
260 | - (UIBarButtonItem *)rightBarButtonItem
261 | {
262 | if (!_rightBarButtonItem)
263 | {
264 | _rightBarButtonItem =[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRewind
265 | target:self.slideMenuController
266 | action:@selector(toggleMenuAnimated:)];
267 | }
268 |
269 | return _rightBarButtonItem;
270 | }
271 |
272 |
273 | - (void)updateBarButtonsAccordingToSlideMenuControllerDirectionAnimated:(BOOL)animated
274 | {
275 | if (self.slideMenuController.slideDirection == NVSlideMenuControllerSlideFromLeftToRight)
276 | {
277 | [self.navigationItem setLeftBarButtonItem:self.leftBarButtonItem animated:animated];
278 | [self.navigationItem setRightBarButtonItem:nil animated:animated];
279 | }
280 | else if (self.slideMenuController.slideDirection == NVSlideMenuControllerSlideFromRightToLeft)
281 | {
282 | [self.navigationItem setRightBarButtonItem:self.rightBarButtonItem animated:animated];
283 | [self.navigationItem setLeftBarButtonItem:nil animated:animated];
284 | }
285 | }
286 |
287 |
288 | #pragma mark - Rotation
289 |
290 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
291 | {
292 | return toInterfaceOrientation != UIInterfaceOrientationPortraitUpsideDown;
293 | }
294 |
295 | @end
296 |
--------------------------------------------------------------------------------
/Demos/Basic/BasicDemo.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 3727E3A6169709A800AA50DA /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3727E3A5169709A800AA50DA /* QuartzCore.framework */; };
11 | 373F73BA1694238700134653 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 373F73B91694238700134653 /* UIKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
12 | 373F73BC1694238700134653 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 373F73BB1694238700134653 /* Foundation.framework */; };
13 | 373F73BE1694238700134653 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 373F73BD1694238700134653 /* CoreGraphics.framework */; };
14 | 373F73C41694238700134653 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 373F73C21694238700134653 /* InfoPlist.strings */; };
15 | 373F73C61694238700134653 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 373F73C51694238700134653 /* main.m */; };
16 | 373F73CA1694238700134653 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 373F73C91694238700134653 /* AppDelegate.m */; };
17 | 373F73CC1694238700134653 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 373F73CB1694238700134653 /* Default.png */; };
18 | 373F73CE1694238700134653 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 373F73CD1694238700134653 /* Default@2x.png */; };
19 | 373F73D01694238700134653 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 373F73CF1694238700134653 /* Default-568h@2x.png */; };
20 | 373F73DC169423CD00134653 /* DetailsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 373F73D8169423CD00134653 /* DetailsViewController.m */; };
21 | 373F73DD169423CD00134653 /* DetailsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 373F73D9169423CD00134653 /* DetailsViewController.xib */; };
22 | 373F73DE169423CD00134653 /* MenuViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 373F73DB169423CD00134653 /* MenuViewController.m */; };
23 | 37D1DAFA17FF02B800C250D7 /* NVSlideMenuController.m in Sources */ = {isa = PBXBuildFile; fileRef = 373F73E2169423D600134653 /* NVSlideMenuController.m */; };
24 | /* End PBXBuildFile section */
25 |
26 | /* Begin PBXFileReference section */
27 | 3727E3A5169709A800AA50DA /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
28 | 373F73B51694238700134653 /* BasicDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BasicDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
29 | 373F73B91694238700134653 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
30 | 373F73BB1694238700134653 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
31 | 373F73BD1694238700134653 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
32 | 373F73C11694238700134653 /* BasicDemo-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "BasicDemo-Info.plist"; sourceTree = ""; };
33 | 373F73C31694238700134653 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; };
34 | 373F73C51694238700134653 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
35 | 373F73C71694238700134653 /* BasicDemo-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "BasicDemo-Prefix.pch"; sourceTree = ""; };
36 | 373F73C81694238700134653 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
37 | 373F73C91694238700134653 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
38 | 373F73CB1694238700134653 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; };
39 | 373F73CD1694238700134653 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = ""; };
40 | 373F73CF1694238700134653 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; };
41 | 373F73D7169423CD00134653 /* DetailsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DetailsViewController.h; sourceTree = ""; };
42 | 373F73D8169423CD00134653 /* DetailsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DetailsViewController.m; sourceTree = ""; };
43 | 373F73D9169423CD00134653 /* DetailsViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DetailsViewController.xib; sourceTree = ""; };
44 | 373F73DA169423CD00134653 /* MenuViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MenuViewController.h; sourceTree = ""; };
45 | 373F73DB169423CD00134653 /* MenuViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MenuViewController.m; sourceTree = ""; };
46 | 373F73E1169423D600134653 /* NVSlideMenuController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NVSlideMenuController.h; sourceTree = ""; };
47 | 373F73E2169423D600134653 /* NVSlideMenuController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NVSlideMenuController.m; sourceTree = ""; };
48 | /* End PBXFileReference section */
49 |
50 | /* Begin PBXFrameworksBuildPhase section */
51 | 373F73B21694238700134653 /* Frameworks */ = {
52 | isa = PBXFrameworksBuildPhase;
53 | buildActionMask = 2147483647;
54 | files = (
55 | 3727E3A6169709A800AA50DA /* QuartzCore.framework in Frameworks */,
56 | 373F73BA1694238700134653 /* UIKit.framework in Frameworks */,
57 | 373F73BC1694238700134653 /* Foundation.framework in Frameworks */,
58 | 373F73BE1694238700134653 /* CoreGraphics.framework in Frameworks */,
59 | );
60 | runOnlyForDeploymentPostprocessing = 0;
61 | };
62 | /* End PBXFrameworksBuildPhase section */
63 |
64 | /* Begin PBXGroup section */
65 | 373F73AA1694238700134653 = {
66 | isa = PBXGroup;
67 | children = (
68 | 373F73BF1694238700134653 /* BasicDemo */,
69 | 373F73B81694238700134653 /* Frameworks */,
70 | 373F73B61694238700134653 /* Products */,
71 | );
72 | sourceTree = "";
73 | };
74 | 373F73B61694238700134653 /* Products */ = {
75 | isa = PBXGroup;
76 | children = (
77 | 373F73B51694238700134653 /* BasicDemo.app */,
78 | );
79 | name = Products;
80 | sourceTree = "";
81 | };
82 | 373F73B81694238700134653 /* Frameworks */ = {
83 | isa = PBXGroup;
84 | children = (
85 | 3727E3A5169709A800AA50DA /* QuartzCore.framework */,
86 | 373F73B91694238700134653 /* UIKit.framework */,
87 | 373F73BB1694238700134653 /* Foundation.framework */,
88 | 373F73BD1694238700134653 /* CoreGraphics.framework */,
89 | );
90 | name = Frameworks;
91 | sourceTree = "";
92 | };
93 | 373F73BF1694238700134653 /* BasicDemo */ = {
94 | isa = PBXGroup;
95 | children = (
96 | 373F73DF169423D600134653 /* lib */,
97 | 373F73C81694238700134653 /* AppDelegate.h */,
98 | 373F73C91694238700134653 /* AppDelegate.m */,
99 | 373F73D6169423CD00134653 /* View Controllers */,
100 | 373F73C01694238700134653 /* Supporting Files */,
101 | );
102 | path = BasicDemo;
103 | sourceTree = "";
104 | };
105 | 373F73C01694238700134653 /* Supporting Files */ = {
106 | isa = PBXGroup;
107 | children = (
108 | 373F73C11694238700134653 /* BasicDemo-Info.plist */,
109 | 373F73C21694238700134653 /* InfoPlist.strings */,
110 | 373F73C51694238700134653 /* main.m */,
111 | 373F73C71694238700134653 /* BasicDemo-Prefix.pch */,
112 | 373F73CB1694238700134653 /* Default.png */,
113 | 373F73CD1694238700134653 /* Default@2x.png */,
114 | 373F73CF1694238700134653 /* Default-568h@2x.png */,
115 | );
116 | name = "Supporting Files";
117 | sourceTree = "";
118 | };
119 | 373F73D6169423CD00134653 /* View Controllers */ = {
120 | isa = PBXGroup;
121 | children = (
122 | 373F73D7169423CD00134653 /* DetailsViewController.h */,
123 | 373F73D8169423CD00134653 /* DetailsViewController.m */,
124 | 373F73D9169423CD00134653 /* DetailsViewController.xib */,
125 | 373F73DA169423CD00134653 /* MenuViewController.h */,
126 | 373F73DB169423CD00134653 /* MenuViewController.m */,
127 | );
128 | path = "View Controllers";
129 | sourceTree = "";
130 | };
131 | 373F73DF169423D600134653 /* lib */ = {
132 | isa = PBXGroup;
133 | children = (
134 | 373F73E0169423D600134653 /* NVSlideMenuController */,
135 | );
136 | name = lib;
137 | path = ../../../lib;
138 | sourceTree = "";
139 | };
140 | 373F73E0169423D600134653 /* NVSlideMenuController */ = {
141 | isa = PBXGroup;
142 | children = (
143 | 373F73E1169423D600134653 /* NVSlideMenuController.h */,
144 | 373F73E2169423D600134653 /* NVSlideMenuController.m */,
145 | );
146 | path = NVSlideMenuController;
147 | sourceTree = "";
148 | };
149 | /* End PBXGroup section */
150 |
151 | /* Begin PBXNativeTarget section */
152 | 373F73B41694238700134653 /* BasicDemo */ = {
153 | isa = PBXNativeTarget;
154 | buildConfigurationList = 373F73D31694238700134653 /* Build configuration list for PBXNativeTarget "BasicDemo" */;
155 | buildPhases = (
156 | 373F73B11694238700134653 /* Sources */,
157 | 373F73B21694238700134653 /* Frameworks */,
158 | 373F73B31694238700134653 /* Resources */,
159 | );
160 | buildRules = (
161 | );
162 | dependencies = (
163 | );
164 | name = BasicDemo;
165 | productName = NVSlideMenuControllerDemo;
166 | productReference = 373F73B51694238700134653 /* BasicDemo.app */;
167 | productType = "com.apple.product-type.application";
168 | };
169 | /* End PBXNativeTarget section */
170 |
171 | /* Begin PBXProject section */
172 | 373F73AC1694238700134653 /* Project object */ = {
173 | isa = PBXProject;
174 | attributes = {
175 | LastUpgradeCheck = 0720;
176 | ORGANIZATIONNAME = "Nicolas VERINAUD";
177 | TargetAttributes = {
178 | 373F73B41694238700134653 = {
179 | DevelopmentTeam = 4C7SPGJ9DJ;
180 | };
181 | };
182 | };
183 | buildConfigurationList = 373F73AF1694238700134653 /* Build configuration list for PBXProject "BasicDemo" */;
184 | compatibilityVersion = "Xcode 3.2";
185 | developmentRegion = English;
186 | hasScannedForEncodings = 0;
187 | knownRegions = (
188 | en,
189 | );
190 | mainGroup = 373F73AA1694238700134653;
191 | productRefGroup = 373F73B61694238700134653 /* Products */;
192 | projectDirPath = "";
193 | projectRoot = "";
194 | targets = (
195 | 373F73B41694238700134653 /* BasicDemo */,
196 | );
197 | };
198 | /* End PBXProject section */
199 |
200 | /* Begin PBXResourcesBuildPhase section */
201 | 373F73B31694238700134653 /* Resources */ = {
202 | isa = PBXResourcesBuildPhase;
203 | buildActionMask = 2147483647;
204 | files = (
205 | 373F73C41694238700134653 /* InfoPlist.strings in Resources */,
206 | 373F73CC1694238700134653 /* Default.png in Resources */,
207 | 373F73CE1694238700134653 /* Default@2x.png in Resources */,
208 | 373F73D01694238700134653 /* Default-568h@2x.png in Resources */,
209 | 373F73DD169423CD00134653 /* DetailsViewController.xib in Resources */,
210 | );
211 | runOnlyForDeploymentPostprocessing = 0;
212 | };
213 | /* End PBXResourcesBuildPhase section */
214 |
215 | /* Begin PBXSourcesBuildPhase section */
216 | 373F73B11694238700134653 /* Sources */ = {
217 | isa = PBXSourcesBuildPhase;
218 | buildActionMask = 2147483647;
219 | files = (
220 | 373F73C61694238700134653 /* main.m in Sources */,
221 | 373F73CA1694238700134653 /* AppDelegate.m in Sources */,
222 | 373F73DC169423CD00134653 /* DetailsViewController.m in Sources */,
223 | 37D1DAFA17FF02B800C250D7 /* NVSlideMenuController.m in Sources */,
224 | 373F73DE169423CD00134653 /* MenuViewController.m in Sources */,
225 | );
226 | runOnlyForDeploymentPostprocessing = 0;
227 | };
228 | /* End PBXSourcesBuildPhase section */
229 |
230 | /* Begin PBXVariantGroup section */
231 | 373F73C21694238700134653 /* InfoPlist.strings */ = {
232 | isa = PBXVariantGroup;
233 | children = (
234 | 373F73C31694238700134653 /* en */,
235 | );
236 | name = InfoPlist.strings;
237 | sourceTree = "";
238 | };
239 | /* End PBXVariantGroup section */
240 |
241 | /* Begin XCBuildConfiguration section */
242 | 373F73D11694238700134653 /* Debug */ = {
243 | isa = XCBuildConfiguration;
244 | buildSettings = {
245 | ALWAYS_SEARCH_USER_PATHS = NO;
246 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
247 | CLANG_CXX_LIBRARY = "libc++";
248 | CLANG_WARN_EMPTY_BODY = YES;
249 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
250 | COPY_PHASE_STRIP = NO;
251 | ENABLE_TESTABILITY = YES;
252 | GCC_C_LANGUAGE_STANDARD = gnu99;
253 | GCC_DYNAMIC_NO_PIC = NO;
254 | GCC_OPTIMIZATION_LEVEL = 0;
255 | GCC_PREPROCESSOR_DEFINITIONS = (
256 | "DEBUG=1",
257 | "$(inherited)",
258 | );
259 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
260 | GCC_WARN_ABOUT_RETURN_TYPE = YES;
261 | GCC_WARN_UNINITIALIZED_AUTOS = YES;
262 | GCC_WARN_UNUSED_VARIABLE = YES;
263 | IPHONEOS_DEPLOYMENT_TARGET = 6.0;
264 | ONLY_ACTIVE_ARCH = YES;
265 | SDKROOT = iphoneos;
266 | TARGETED_DEVICE_FAMILY = "1,2";
267 | };
268 | name = Debug;
269 | };
270 | 373F73D21694238700134653 /* Release */ = {
271 | isa = XCBuildConfiguration;
272 | buildSettings = {
273 | ALWAYS_SEARCH_USER_PATHS = NO;
274 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
275 | CLANG_CXX_LIBRARY = "libc++";
276 | CLANG_WARN_EMPTY_BODY = YES;
277 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
278 | COPY_PHASE_STRIP = YES;
279 | GCC_C_LANGUAGE_STANDARD = gnu99;
280 | GCC_WARN_ABOUT_RETURN_TYPE = YES;
281 | GCC_WARN_UNINITIALIZED_AUTOS = YES;
282 | GCC_WARN_UNUSED_VARIABLE = YES;
283 | IPHONEOS_DEPLOYMENT_TARGET = 6.0;
284 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
285 | SDKROOT = iphoneos;
286 | TARGETED_DEVICE_FAMILY = "1,2";
287 | VALIDATE_PRODUCT = YES;
288 | };
289 | name = Release;
290 | };
291 | 373F73D41694238700134653 /* Debug */ = {
292 | isa = XCBuildConfiguration;
293 | buildSettings = {
294 | CLANG_ENABLE_OBJC_ARC = YES;
295 | CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES;
296 | CODE_SIGN_IDENTITY = "iPhone Developer";
297 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
298 | GCC_PREFIX_HEADER = "BasicDemo/BasicDemo-Prefix.pch";
299 | INFOPLIST_FILE = "$(SRCROOT)/BasicDemo/BasicDemo-Info.plist";
300 | IPHONEOS_DEPLOYMENT_TARGET = 5.0;
301 | PRODUCT_BUNDLE_IDENTIFIER = "com.nverinaud.${PRODUCT_NAME:rfc1034identifier}";
302 | PRODUCT_NAME = BasicDemo;
303 | PROVISIONING_PROFILE = "";
304 | WARNING_CFLAGS = "-Warc-bridge-casts-disallowed-in-nonarc";
305 | WRAPPER_EXTENSION = app;
306 | };
307 | name = Debug;
308 | };
309 | 373F73D51694238700134653 /* Release */ = {
310 | isa = XCBuildConfiguration;
311 | buildSettings = {
312 | CLANG_ENABLE_OBJC_ARC = YES;
313 | CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES;
314 | CODE_SIGN_IDENTITY = "iPhone Developer";
315 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
316 | GCC_PREFIX_HEADER = "BasicDemo/BasicDemo-Prefix.pch";
317 | INFOPLIST_FILE = "$(SRCROOT)/BasicDemo/BasicDemo-Info.plist";
318 | IPHONEOS_DEPLOYMENT_TARGET = 5.0;
319 | PRODUCT_BUNDLE_IDENTIFIER = "com.nverinaud.${PRODUCT_NAME:rfc1034identifier}";
320 | PRODUCT_NAME = BasicDemo;
321 | PROVISIONING_PROFILE = "";
322 | WARNING_CFLAGS = "-Warc-bridge-casts-disallowed-in-nonarc";
323 | WRAPPER_EXTENSION = app;
324 | };
325 | name = Release;
326 | };
327 | /* End XCBuildConfiguration section */
328 |
329 | /* Begin XCConfigurationList section */
330 | 373F73AF1694238700134653 /* Build configuration list for PBXProject "BasicDemo" */ = {
331 | isa = XCConfigurationList;
332 | buildConfigurations = (
333 | 373F73D11694238700134653 /* Debug */,
334 | 373F73D21694238700134653 /* Release */,
335 | );
336 | defaultConfigurationIsVisible = 0;
337 | defaultConfigurationName = Release;
338 | };
339 | 373F73D31694238700134653 /* Build configuration list for PBXNativeTarget "BasicDemo" */ = {
340 | isa = XCConfigurationList;
341 | buildConfigurations = (
342 | 373F73D41694238700134653 /* Debug */,
343 | 373F73D51694238700134653 /* Release */,
344 | );
345 | defaultConfigurationIsVisible = 0;
346 | defaultConfigurationName = Release;
347 | };
348 | /* End XCConfigurationList section */
349 | };
350 | rootObject = 373F73AC1694238700134653 /* Project object */;
351 | }
352 |
--------------------------------------------------------------------------------
/Demos/Basic/BasicDemo/View Controllers/DetailsViewController.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1552
5 | 12D78
6 | 3084
7 | 1187.37
8 | 626.00
9 |
13 |
14 | IBProxyObject
15 | IBUIBarButtonItem
16 | IBUIButton
17 | IBUILabel
18 | IBUISwitch
19 | IBUITextView
20 | IBUIToolbar
21 | IBUIView
22 |
23 |
24 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin
25 |
26 |
30 |
31 |
35 |
39 |
245 |
246 |
247 | 266
248 | {320, 44}
249 |
250 |
251 |
252 | _NS:9
253 | NO
254 | NO
255 | IBCocoaTouchFramework
256 | 2
257 |
258 |
259 | IBCocoaTouchFramework
260 |
261 | 5
262 |
263 |
264 | IBCocoaTouchFramework
265 | 2
266 |
267 | 0
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 | view
277 |
278 |
279 |
280 | 3
281 |
282 |
283 |
284 | textView
285 |
286 |
287 |
288 | 12
289 |
290 |
291 |
292 | bounceSwitch
293 |
294 |
295 |
296 | 20
297 |
298 |
299 |
300 | menuWidthSwitch
301 |
302 |
303 |
304 | 24
305 |
306 |
307 |
308 | toolbar
309 |
310 |
311 |
312 | 29
313 |
314 |
315 |
316 | showMenu:
317 |
318 |
319 | 7
320 |
321 | 14
322 |
323 |
324 |
325 | changeSlideDirection:
326 |
327 |
328 | 7
329 |
330 | 16
331 |
332 |
333 |
334 | bounceSwitchValueChanged:
335 |
336 |
337 | 13
338 |
339 | 19
340 |
341 |
342 |
343 | menuWidthSwitchValueChanged:
344 |
345 |
346 | 13
347 |
348 | 25
349 |
350 |
351 |
352 | hideKeyboard:
353 |
354 |
355 |
356 | 30
357 |
358 |
359 |
360 |
361 |
362 | 0
363 |
364 |
365 |
366 |
367 |
368 | 1
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 | -1
383 |
384 |
385 | File's Owner
386 |
387 |
388 | -2
389 |
390 |
391 |
392 |
393 | 7
394 |
395 |
396 |
397 |
398 | 13
399 |
400 |
401 |
402 |
403 | 15
404 |
405 |
406 |
407 |
408 | 17
409 |
410 |
411 |
412 |
413 | 18
414 |
415 |
416 |
417 |
418 | 21
419 |
420 |
421 |
422 |
423 | 22
424 |
425 |
426 |
427 |
428 | 26
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 | 27
438 |
439 |
440 |
441 |
442 | 28
443 |
444 |
445 |
446 |
447 |
448 |
449 | DetailsViewController
450 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin
451 | UIResponder
452 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin
453 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin
454 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin
455 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin
456 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin
457 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin
458 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin
459 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin
460 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin
461 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin
462 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin
463 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin
464 |
465 |
466 |
467 |
468 |
469 | 30
470 |
471 |
472 |
473 |
474 | DetailsViewController
475 | UIViewController
476 |
477 | id
478 | id
479 | id
480 | id
481 | id
482 |
483 |
484 |
485 | bounceSwitchValueChanged:
486 | id
487 |
488 |
489 | changeSlideDirection:
490 | id
491 |
492 |
493 | hideKeyboard:
494 | id
495 |
496 |
497 | menuWidthSwitchValueChanged:
498 | id
499 |
500 |
501 | showMenu:
502 | id
503 |
504 |
505 |
506 | UISwitch
507 | UISwitch
508 | UITextView
509 | UIToolbar
510 |
511 |
512 |
513 | bounceSwitch
514 | UISwitch
515 |
516 |
517 | menuWidthSwitch
518 | UISwitch
519 |
520 |
521 | textView
522 | UITextView
523 |
524 |
525 | toolbar
526 | UIToolbar
527 |
528 |
529 |
530 | IBProjectSource
531 | ./Classes/DetailsViewController.h
532 |
533 |
534 |
535 |
536 | 0
537 | IBCocoaTouchFramework
538 | YES
539 | 3
540 | 2083
541 |
542 |
543 |
--------------------------------------------------------------------------------
/lib/NVSlideMenuController/NVSlideMenuController.m:
--------------------------------------------------------------------------------
1 | //
2 | // NVSlideMenuViewController.m
3 | // NVSlideMenuViewController
4 | //
5 | // Created by Nicolas Verinaud on 31/12/12.
6 | // Copyright (c) 2012 Nicolas Verinaud. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "NVSlideMenuController.h"
11 |
12 | #ifdef __has_feature
13 | #define OBJC_ARC_ENABLED __has_feature(objc_arc)
14 | #else
15 | #define OBJC_ARC_ENABLED 0
16 | #endif
17 |
18 |
19 | #define ANIMATION_DURATION 0.3f
20 | #define BOUNCE_DURATION 0.3f
21 |
22 | @interface NVSlideMenuController ()
23 | {
24 | CGFloat _contentViewWidthWhenMenuIsOpen;
25 | }
26 |
27 | /** Children view controllers */
28 | @property (nonatomic, readwrite, strong) UIViewController *menuViewController;
29 | @property (nonatomic, readwrite, strong) UIViewController *contentViewController;
30 |
31 | /** Shadow */
32 | - (void)setShadowOnContentView;
33 | - (void)removeShadowOnContentView;
34 | - (CGSize)shadowOffsetAccordingToCurrentSlideDirection;
35 |
36 | /**
37 | Load the menu view controller view and add its view as a subview
38 | to self.view with correct frame.
39 | */
40 | - (void)loadMenuViewControllerViewIfNeeded;
41 |
42 | /** Gesture recognizers */
43 | @property (nonatomic, strong) UITapGestureRecognizer *tapGesture;
44 | - (void)tapGestureTriggered:(UITapGestureRecognizer *)tapGesture;
45 |
46 | @property (nonatomic, assign) CGRect contentViewControllerFrame;
47 | @property (nonatomic, assign) BOOL menuWasOpenAtPanBegin;
48 | @property (nonatomic, strong) UIPanGestureRecognizer *panGesture;
49 | - (void)panGestureTriggered:(UIPanGestureRecognizer *)panGesture;
50 |
51 | /** Utils */
52 | - (CGFloat)contentViewMinX;
53 | - (CGRect)menuViewFrameAccordingToCurrentSlideDirection;
54 | - (UIViewAutoresizing)menuViewAutoresizingMaskAccordingToCurrentSlideDirection;
55 |
56 |
57 | /** State */
58 | @property (nonatomic, assign, getter = isContentViewHidden) BOOL contentViewHidden;
59 |
60 | @end
61 |
62 |
63 | @implementation NVSlideMenuController
64 |
65 | #pragma mark - Memory Management
66 |
67 | #if !OBJC_ARC_ENABLED
68 | - (void)dealloc
69 | {
70 | [_menuViewController release];
71 | [_contentViewController release];
72 |
73 | [_tapGesture release];
74 | [_panGesture release];
75 |
76 | [super dealloc];
77 | }
78 | #endif
79 |
80 |
81 | #pragma mark - Creation
82 | #pragma mark Designated Initializer
83 |
84 | - (id)initWithMenuViewController:(UIViewController *)menuViewController andContentViewController:(UIViewController *)contentViewController
85 | {
86 | self = [super initWithNibName:nil bundle:nil];
87 | if (self)
88 | {
89 | self.menuViewController = menuViewController;
90 | self.contentViewController = contentViewController;
91 | self.panGestureEnabled = YES;
92 | self.slideDirection = NVSlideMenuControllerSlideFromLeftToRight;
93 | _contentViewWidthWhenMenuIsOpen = -1;
94 | self.menuWidth = 276;
95 | self.autoAdjustMenuWidth = YES;
96 | self.showShadowOnContentView = YES;
97 | }
98 |
99 | return self;
100 | }
101 |
102 | #pragma mark Overriden Initializers
103 |
104 | - (id)initWithCoder:(NSCoder *)aDecoder
105 | {
106 | self = [super initWithCoder:aDecoder];
107 | if (self)
108 | {
109 | self.menuViewController = nil;
110 | self.contentViewController = nil;
111 | self.panGestureEnabled = YES;
112 | self.slideDirection = NVSlideMenuControllerSlideFromLeftToRight;
113 | self.autoAdjustMenuWidth = YES;
114 | self.showShadowOnContentView = YES;
115 | }
116 |
117 | return self;
118 | }
119 |
120 |
121 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
122 | {
123 | return [self initWithMenuViewController:nil andContentViewController:nil];
124 | }
125 |
126 |
127 | #pragma mark - Children View Controllers
128 |
129 | - (void)setMenuViewController:(UIViewController *)menuViewController
130 | {
131 | if (menuViewController != _menuViewController)
132 | {
133 | [_menuViewController willMoveToParentViewController:nil];
134 | [_menuViewController removeFromParentViewController];
135 |
136 | #if !OBJC_ARC_ENABLED
137 | [_menuViewController release];
138 | _menuViewController = [menuViewController retain];
139 | #else
140 | _menuViewController = menuViewController;
141 | #endif
142 | }
143 | }
144 |
145 |
146 | - (void)setContentViewController:(UIViewController *)contentViewController
147 | {
148 | if (contentViewController != _contentViewController)
149 | {
150 | [_contentViewController willMoveToParentViewController:nil];
151 | [_contentViewController removeFromParentViewController];
152 |
153 | #if !OBJC_ARC_ENABLED
154 | [_contentViewController release];
155 | _contentViewController = [contentViewController retain];
156 | #else
157 | _contentViewController = contentViewController;
158 | #endif
159 | }
160 | }
161 |
162 |
163 | #pragma mark - View lifecycle
164 |
165 | - (void)viewDidLoad
166 | {
167 | [super viewDidLoad];
168 |
169 | if (_contentViewWidthWhenMenuIsOpen >= 0)
170 | self.menuWidth = CGRectGetWidth(self.view.bounds) - _contentViewWidthWhenMenuIsOpen;
171 |
172 | [self addChildViewController:self.contentViewController];
173 | self.contentViewController.view.frame = self.view.bounds;
174 | [self.view addSubview:self.contentViewController.view];
175 | [self.contentViewController didMoveToParentViewController:self];
176 |
177 | [self setShadowOnContentView];
178 |
179 | [self.contentViewController.view addGestureRecognizer:self.tapGesture];
180 | self.tapGesture.enabled = NO;
181 | [self.contentViewController.view addGestureRecognizer:self.panGesture];
182 | }
183 |
184 |
185 | - (void)viewWillAppear:(BOOL)animated
186 | {
187 | [super viewWillAppear:animated];
188 |
189 | if (![self isMenuOpen])
190 | self.contentViewController.view.frame = self.view.bounds;
191 |
192 | [self.contentViewController beginAppearanceTransition:YES animated:animated];
193 | if ([self.menuViewController isViewLoaded] && self.menuViewController.view.superview)
194 | [self.menuViewController beginAppearanceTransition:YES animated:animated];
195 | }
196 |
197 |
198 | - (void)viewDidAppear:(BOOL)animated
199 | {
200 | [super viewDidAppear:animated];
201 |
202 | [self.contentViewController endAppearanceTransition];
203 | if ([self.menuViewController isViewLoaded] && self.menuViewController.view.superview)
204 | [self.menuViewController endAppearanceTransition];
205 | }
206 |
207 |
208 | - (void)viewWillDisappear:(BOOL)animated
209 | {
210 | [super viewWillDisappear:animated];
211 |
212 | [self.contentViewController beginAppearanceTransition:NO animated:animated];
213 | if ([self.menuViewController isViewLoaded])
214 | [self.menuViewController beginAppearanceTransition:NO animated:animated];
215 | }
216 |
217 |
218 | - (void)viewDidDisappear:(BOOL)animated
219 | {
220 | [super viewDidDisappear:animated];
221 |
222 | [self.contentViewController endAppearanceTransition];
223 | if ([self.menuViewController isViewLoaded])
224 | [self.menuViewController endAppearanceTransition];
225 | }
226 |
227 |
228 | #pragma mark - Appearance & rotation callbacks
229 |
230 | - (BOOL)shouldAutomaticallyForwardAppearanceMethods
231 | {
232 | return NO;
233 | }
234 |
235 |
236 | - (BOOL)shouldAutomaticallyForwardRotationMethods
237 | {
238 | return YES;
239 | }
240 |
241 |
242 | - (BOOL)automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers
243 | {
244 | return NO;
245 | }
246 |
247 |
248 | - (UIStatusBarStyle)preferredStatusBarStyle
249 | {
250 | UIViewController *vc = self.contentViewController;
251 |
252 | if ([self isMenuOpen])
253 | vc = self.menuViewController;
254 |
255 | return [vc preferredStatusBarStyle];
256 | }
257 |
258 |
259 | - (UIViewController *)childViewControllerForStatusBarStyle
260 | {
261 | UIViewController *vc = self.contentViewController;
262 |
263 | if ([self isMenuOpen])
264 | vc = self.menuViewController;
265 |
266 | return vc;
267 | }
268 |
269 |
270 | - (UIViewController *)childViewControllerForStatusBarHidden
271 | {
272 | UIViewController *vc = self.contentViewController;
273 |
274 | if ([self isMenuOpen])
275 | vc = self.menuViewController;
276 |
277 | return vc;
278 | }
279 |
280 |
281 | #pragma mark - Rotation
282 |
283 | - (BOOL)shouldAutorotate
284 | {
285 | return [self.menuViewController shouldAutorotate] &&
286 | [self.contentViewController shouldAutorotate] &&
287 | self.panGesture.state != UIGestureRecognizerStateChanged;
288 | }
289 |
290 |
291 | - (UIInterfaceOrientationMask)supportedInterfaceOrientations
292 | {
293 | return [self.menuViewController supportedInterfaceOrientations] & [self.contentViewController supportedInterfaceOrientations];
294 | }
295 |
296 |
297 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
298 | {
299 | return [self.menuViewController shouldAutorotateToInterfaceOrientation:toInterfaceOrientation] &&
300 | [self.contentViewController shouldAutorotateToInterfaceOrientation:toInterfaceOrientation];
301 | }
302 |
303 |
304 | - (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
305 | {
306 | [super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
307 |
308 | if ([self isMenuOpen])
309 | {
310 | CGRect frame = self.contentViewController.view.frame;
311 | frame.origin.x = [self contentViewMinX];
312 | self.contentViewController.view.frame = frame;
313 |
314 | if (![self isContentViewHidden])
315 | {
316 | // Fix weird shadow behavior according to this post : http://blog.radi.ws/post/8348898129/calayers-shadowpath-and-uiview-autoresizing
317 | CALayer *layer = self.contentViewController.view.layer;
318 | CGPathRef oldShadowPath = layer.shadowPath;
319 |
320 | if (oldShadowPath)
321 | CFRetain(oldShadowPath);
322 |
323 | [self setShadowOnContentView];
324 |
325 | if (oldShadowPath)
326 | {
327 | CABasicAnimation *transition = [CABasicAnimation animationWithKeyPath:@"shadowPath"];
328 | #if !OBJC_ARC_ENABLED
329 | transition.fromValue = (id)oldShadowPath;
330 | #else
331 | transition.fromValue = (__bridge id)oldShadowPath;
332 | #endif
333 |
334 | transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
335 | transition.duration = duration;
336 | [layer addAnimation:transition forKey:@"transition"];
337 |
338 | CFRelease(oldShadowPath);
339 | }
340 | }
341 | }
342 | }
343 |
344 |
345 | #pragma mark - Shadow
346 |
347 | - (void)setShowShadowOnContentView:(BOOL)showShadowOnContentView
348 | {
349 | if (showShadowOnContentView != _showShadowOnContentView)
350 | {
351 | _showShadowOnContentView = showShadowOnContentView;
352 |
353 | if ([self.contentViewController isViewLoaded])
354 | {
355 | if (_showShadowOnContentView)
356 | [self setShadowOnContentView];
357 | else if (!_showShadowOnContentView)
358 | [self removeShadowOnContentView];
359 | }
360 | }
361 | }
362 |
363 |
364 | - (void)setShadowOnContentView
365 | {
366 | if ([self showShadowOnContentView])
367 | {
368 | UIView *contentView = self.contentViewController.view;
369 | CALayer *layer = contentView.layer;
370 | layer.masksToBounds = NO;
371 | layer.shadowColor = [[UIColor blackColor] CGColor];
372 | layer.shadowOpacity = 1.f;
373 | layer.shadowRadius = 5.f;
374 | layer.shadowPath = [[UIBezierPath bezierPathWithRect:contentView.bounds] CGPath];
375 | layer.shadowOffset = [self shadowOffsetAccordingToCurrentSlideDirection];
376 | }
377 | }
378 |
379 |
380 | - (void)removeShadowOnContentView
381 | {
382 | UIView *contentView = self.contentViewController.view;
383 | CALayer *layer = contentView.layer;
384 | layer.masksToBounds = YES;
385 | }
386 |
387 |
388 | - (CGSize)shadowOffsetAccordingToCurrentSlideDirection
389 | {
390 | if (self.slideDirection == NVSlideMenuControllerSlideFromLeftToRight)
391 | return CGSizeMake(-2.5f, 3.f);
392 | else if (self.slideDirection == NVSlideMenuControllerSlideFromRightToLeft)
393 | return CGSizeMake(2.5f, 3.f);
394 |
395 | return CGSizeZero;
396 | }
397 |
398 |
399 | #pragma mark - Slide Direction
400 |
401 | - (void)setSlideDirection:(NVSlideMenuControllerSlideDirection)slideDirection
402 | {
403 | [self setSlideDirection:slideDirection animated:NO];
404 | }
405 |
406 |
407 | - (void)setSlideDirection:(NVSlideMenuControllerSlideDirection)slideDirection animated:(BOOL)animated
408 | {
409 | if (slideDirection != _slideDirection)
410 | {
411 | _slideDirection = slideDirection;
412 |
413 | if ([self.menuViewController isViewLoaded] && [self.contentViewController isViewLoaded])
414 | {
415 | self.menuViewController.view.autoresizingMask = [self menuViewAutoresizingMaskAccordingToCurrentSlideDirection];
416 |
417 | BOOL menuIsOpen = [self isMenuOpen];
418 | NSTimeInterval duration = (animated && menuIsOpen) ? ANIMATION_DURATION*1.5 : 0;
419 | CGRect targetedContentViewFrame = self.view.bounds;
420 | if (menuIsOpen)
421 | targetedContentViewFrame.origin.x = [self contentViewMinX];
422 |
423 | [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
424 |
425 | self.menuViewController.view.frame = [self menuViewFrameAccordingToCurrentSlideDirection];
426 | self.contentViewController.view.frame = targetedContentViewFrame;
427 |
428 | if (![self isContentViewHidden])
429 | [self setShadowOnContentView];
430 |
431 | } completion:nil];
432 | }
433 | }
434 | }
435 |
436 |
437 | #pragma mark - Menu view lazy load
438 |
439 | - (void)loadMenuViewControllerViewIfNeeded
440 | {
441 | if (!self.menuViewController.view.window)
442 | {
443 | [self addChildViewController:self.menuViewController];
444 | self.menuViewController.view.frame = [self menuViewFrameAccordingToCurrentSlideDirection];
445 | self.menuViewController.view.autoresizingMask = [self menuViewAutoresizingMaskAccordingToCurrentSlideDirection];
446 | [self.view insertSubview:self.menuViewController.view atIndex:0];
447 | [self.menuViewController didMoveToParentViewController:self];
448 | }
449 | }
450 |
451 |
452 | #pragma mark - Navigation
453 |
454 | - (IBAction)toggleMenuAnimated:(id)sender
455 | {
456 | if ([self isMenuOpen])
457 | [self closeMenuAnimated:YES completion:nil];
458 | else
459 | [self openMenuAnimated:YES completion:nil];
460 | }
461 |
462 |
463 | - (void)openMenuAnimated:(BOOL)animated completion:(void(^)(BOOL finished))completion
464 | {
465 | NSTimeInterval duration = animated ? ANIMATION_DURATION : 0;
466 |
467 | UIView *contentView = self.contentViewController.view;
468 | CGRect contentViewFrame = contentView.frame;
469 | contentViewFrame.origin.x = [self contentViewMinX];
470 |
471 | [self loadMenuViewControllerViewIfNeeded];
472 | [self.menuViewController beginAppearanceTransition:YES animated:animated];
473 | [self.contentViewController viewWillSlideOut:animated inSlideMenuController:self];
474 |
475 | [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
476 | contentView.frame = contentViewFrame;
477 | [self setShadowOnContentView];
478 | self.menuViewController.view.frame = [self menuViewFrameAccordingToCurrentSlideDirection];
479 | } completion:^(BOOL finished) {
480 | [self.menuViewController endAppearanceTransition];
481 |
482 | if ([self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)])
483 | [self setNeedsStatusBarAppearanceUpdate];
484 |
485 | [self.contentViewController viewDidSlideOut:animated inSlideMenuController:self];
486 |
487 | self.tapGesture.enabled = YES;
488 |
489 | if (completion)
490 | completion(finished);
491 | }];
492 | }
493 |
494 |
495 | - (void)closeMenuAnimated:(BOOL)animated completion:(void(^)(BOOL finished))completion
496 | {
497 | if ([self isMenuOpen])
498 | {
499 | // Remove gestures
500 | self.tapGesture.enabled = NO;
501 |
502 | self.menuViewController.view.userInteractionEnabled = NO;
503 |
504 | NSTimeInterval duration = animated ? ANIMATION_DURATION : 0;
505 |
506 | UIView *contentView = self.contentViewController.view;
507 | CGRect contentViewFrame = contentView.frame;
508 | contentViewFrame.origin.x = 0;
509 |
510 | [self loadMenuViewControllerViewIfNeeded];
511 | [self.menuViewController beginAppearanceTransition:NO animated:animated];
512 | [self.contentViewController viewWillSlideIn:animated inSlideMenuController:self];
513 |
514 | [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
515 | contentView.frame = contentViewFrame;
516 | } completion:^(BOOL finished) {
517 | [self.menuViewController endAppearanceTransition];
518 |
519 | if ([self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)])
520 | [self setNeedsStatusBarAppearanceUpdate];
521 |
522 | [self.contentViewController viewDidSlideIn:animated inSlideMenuController:self];
523 | self.contentViewHidden = NO;
524 |
525 | self.menuViewController.view.userInteractionEnabled = YES;
526 |
527 | if (completion)
528 | completion(finished);
529 | }];
530 | }
531 | else
532 | {
533 | if (completion)
534 | completion(YES);
535 | }
536 | }
537 |
538 |
539 | - (void)closeMenuBehindContentViewController:(UIViewController *)contentViewController animated:(BOOL)animated completion:(void(^)(BOOL finished))completion
540 | {
541 | [self closeMenuBehindContentViewController:contentViewController animated:animated bounce:self.bounceWhenNavigating completion:completion];
542 | }
543 |
544 |
545 | - (void)closeMenuBehindContentViewController:(UIViewController *)contentViewController animated:(BOOL)animated bounce:(BOOL)bounce completion:(void(^)(BOOL finished))completion
546 | {
547 | NSAssert(contentViewController != nil, @"Can't show a nil content view controller.");
548 |
549 | void (^swapContentViewController)() = nil;
550 |
551 | if (contentViewController != self.contentViewController)
552 | {
553 | swapContentViewController = ^{
554 | // Preserve the frame
555 | CGRect frame = self.contentViewController.view.frame;
556 |
557 | // Remove old content view
558 | [self.contentViewController.view removeGestureRecognizer:self.tapGesture];
559 | [self.contentViewController.view removeGestureRecognizer:self.panGesture];
560 |
561 | BOOL contentViewWasAlreadyHidden = [self isContentViewHidden];
562 | if (!contentViewWasAlreadyHidden)
563 | [self.contentViewController beginAppearanceTransition:NO animated:NO];
564 |
565 | [self.contentViewController willMoveToParentViewController:nil];
566 | [self.contentViewController.view removeFromSuperview];
567 | [self.contentViewController removeFromParentViewController];
568 |
569 | if (!contentViewWasAlreadyHidden)
570 | [self.contentViewController endAppearanceTransition];
571 |
572 | // Add the new content view
573 | self.contentViewController = contentViewController;
574 | self.contentViewController.view.frame = frame;
575 | [self.contentViewController.view addGestureRecognizer:self.tapGesture];
576 | [self.contentViewController.view addGestureRecognizer:self.panGesture];
577 | [self setShadowOnContentView];
578 | [self.contentViewController beginAppearanceTransition:YES animated:NO];
579 | [self addChildViewController:self.contentViewController];
580 | [self.view addSubview:self.contentViewController.view];
581 | [self.contentViewController didMoveToParentViewController:self];
582 | [self.contentViewController endAppearanceTransition];
583 | };
584 | }
585 |
586 | if (bounce && animated && ![self isContentViewHidden])
587 | {
588 | CGFloat offScreenDistance = 10.0f;
589 | CGFloat bounceDistance = offScreenDistance + CGRectGetWidth(self.contentViewController.view.bounds) - self.menuWidth;
590 |
591 | //Invert the bounce distance if needed for the slide direction
592 | if (self.slideDirection == NVSlideMenuControllerSlideFromRightToLeft)
593 | bounceDistance *= -1;
594 |
595 | UIView *contentView = self.contentViewController.view;
596 | CGRect contentBounceFrame = contentView.frame;
597 | contentBounceFrame.origin.x += bounceDistance;
598 |
599 | CGRect menuBounceFrame = self.menuViewController.view.frame;
600 | menuBounceFrame.size.width += bounceDistance;
601 |
602 | [UIView animateWithDuration:BOUNCE_DURATION delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
603 | contentView.frame = contentBounceFrame;
604 | self.menuViewController.view.frame = menuBounceFrame;
605 | } completion:^(BOOL finished) {
606 | if (swapContentViewController)
607 | swapContentViewController();
608 |
609 | [self closeMenuAnimated:animated completion:completion];
610 | }];
611 |
612 | }
613 | else
614 | {
615 | if (swapContentViewController)
616 | swapContentViewController();
617 |
618 | // Perform the close animation
619 | [self closeMenuAnimated:animated completion:completion];
620 | }
621 | }
622 |
623 |
624 | - (void)hideContentViewControllerAnimated:(BOOL)animated completion:(void(^)(BOOL finished))completion
625 | {
626 | if (![self isContentViewHidden])
627 | {
628 | NSTimeInterval duration = animated ? ANIMATION_DURATION/2 : 0;
629 | BOOL menuIsOpen = [self isMenuOpen];
630 | UIView *contentView = self.contentViewController.view;
631 | CGRect contentViewFrame = contentView.frame;
632 |
633 | self.contentViewHidden = YES;
634 | contentViewFrame.origin.x = [self contentViewMinX];
635 |
636 | [self loadMenuViewControllerViewIfNeeded];
637 |
638 | if (!menuIsOpen)
639 | [self.menuViewController beginAppearanceTransition:YES animated:animated];
640 |
641 | [self.contentViewController beginAppearanceTransition:NO animated:animated];
642 |
643 | [UIView animateWithDuration:duration animations:^{
644 | contentView.frame = contentViewFrame;
645 | [self removeShadowOnContentView];
646 | self.menuViewController.view.frame = self.view.bounds;
647 | } completion:^(BOOL finished) {
648 | if (!menuIsOpen)
649 | [self.menuViewController endAppearanceTransition];
650 |
651 | [self.contentViewController endAppearanceTransition];
652 |
653 | if (completion)
654 | completion(finished);
655 | }];
656 | }
657 | }
658 |
659 |
660 | - (void)partiallyShowContentViewControllerAnimated:(BOOL)animated completion:(void(^)(BOOL finished))completion
661 | {
662 | if ([self isContentViewHidden])
663 | {
664 | NSTimeInterval duration = animated ? ANIMATION_DURATION/2 : 0;
665 | UIView *contentView = self.contentViewController.view;
666 | CGRect contentViewFrame = contentView.frame;
667 |
668 | self.contentViewHidden = NO;
669 | contentViewFrame.origin.x = [self contentViewMinX];
670 |
671 | [self.contentViewController beginAppearanceTransition:YES animated:animated];
672 |
673 | [UIView animateWithDuration:duration animations:^{
674 | contentView.frame = contentViewFrame;
675 | [self setShadowOnContentView];
676 | self.menuViewController.view.frame = [self menuViewFrameAccordingToCurrentSlideDirection];
677 | } completion:^(BOOL finished) {
678 | [self.contentViewController endAppearanceTransition];
679 |
680 | if (completion)
681 | completion(finished);
682 | }];
683 | }
684 | }
685 |
686 |
687 | #pragma mark - Gestures
688 |
689 | - (UITapGestureRecognizer *)tapGesture
690 | {
691 | if (!_tapGesture)
692 | _tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestureTriggered:)];
693 |
694 | return _tapGesture;
695 | }
696 |
697 |
698 | - (void)tapGestureTriggered:(UITapGestureRecognizer *)tapGesture
699 | {
700 | if (tapGesture.state == UIGestureRecognizerStateEnded)
701 | [self closeMenuAnimated:YES completion:nil];
702 | }
703 |
704 |
705 | - (UIPanGestureRecognizer *)panGesture
706 | {
707 | if (!_panGesture)
708 | _panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGestureTriggered:)];
709 |
710 | return _panGesture;
711 | }
712 |
713 |
714 | - (void)setPanGestureEnabled:(BOOL)panGestureEnabled
715 | {
716 | self.panGesture.enabled = panGestureEnabled;
717 | }
718 |
719 |
720 | - (BOOL)panGestureEnabled
721 | {
722 | return self.panGesture.enabled;
723 | }
724 |
725 |
726 | - (void)panGestureTriggered:(UIPanGestureRecognizer *)panGesture
727 | {
728 | if (panGesture.state == UIGestureRecognizerStateBegan)
729 | {
730 | self.contentViewControllerFrame = self.contentViewController.view.frame;
731 | self.menuWasOpenAtPanBegin = [self isMenuOpen];
732 |
733 | if (!self.menuWasOpenAtPanBegin)
734 | {
735 | [self loadMenuViewControllerViewIfNeeded]; // Menu is closed, load it if needed
736 | [self.menuViewController beginAppearanceTransition:YES animated:YES]; // Menu is appearing
737 | [self.contentViewController viewWillSlideOut:YES inSlideMenuController:self]; // Content view controller is sliding out
738 | }
739 | else
740 | {
741 | [self.contentViewController viewWillSlideIn:YES inSlideMenuController:self];
742 | }
743 | }
744 |
745 | CGPoint translation = [panGesture translationInView:panGesture.view];
746 |
747 | CGRect frame = self.contentViewControllerFrame;
748 | CGFloat contentViewMinX = [self contentViewMinX];
749 |
750 | if (self.slideDirection == NVSlideMenuControllerSlideFromLeftToRight)
751 | {
752 | frame.origin.x += translation.x;
753 |
754 | if (frame.origin.x > contentViewMinX)
755 | frame.origin.x = contentViewMinX;
756 | else if (frame.origin.x < 0)
757 | frame.origin.x = 0;
758 | }
759 | else
760 | {
761 | frame.origin.x += translation.x;
762 |
763 | if (frame.origin.x < contentViewMinX)
764 | frame.origin.x = contentViewMinX;
765 | else if (frame.origin.x > 0)
766 | frame.origin.x = 0;
767 | }
768 |
769 | panGesture.view.frame = frame;
770 |
771 | if (panGesture.state == UIGestureRecognizerStateEnded)
772 | {
773 | CGPoint velocity = [panGesture velocityInView:panGesture.view];
774 | CGFloat distance = 0;
775 | NSTimeInterval animationDuration = 0;
776 |
777 | BOOL close = NO;
778 | if (self.slideDirection == NVSlideMenuControllerSlideFromLeftToRight)
779 | close = (velocity.x < 0);
780 | else
781 | close = (velocity.x > 0);
782 |
783 | if (close) // Close
784 | {
785 | // Compute animation duration
786 | distance = frame.origin.x;
787 | animationDuration = fabs(distance / velocity.x);
788 | if (animationDuration > ANIMATION_DURATION)
789 | animationDuration = ANIMATION_DURATION;
790 |
791 | // Remove gestures
792 | self.tapGesture.enabled = NO;
793 |
794 | frame.origin.x = 0;
795 |
796 | if (!self.menuWasOpenAtPanBegin)
797 | {
798 | [self.menuViewController endAppearanceTransition];
799 | [self.contentViewController viewDidSlideOut:YES inSlideMenuController:self];
800 | [self.contentViewController viewWillSlideIn:YES inSlideMenuController:self];
801 | }
802 |
803 | [self.menuViewController beginAppearanceTransition:NO animated:YES];
804 |
805 | [UIView animateWithDuration:animationDuration delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
806 | self.contentViewController.view.frame = frame;
807 | } completion:^(BOOL finished) {
808 | [self.menuViewController endAppearanceTransition];
809 |
810 | if ([self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)])
811 | [self setNeedsStatusBarAppearanceUpdate];
812 |
813 | [self.contentViewController viewDidSlideIn:YES inSlideMenuController:self];
814 | }];
815 | }
816 | else // Open
817 | {
818 | distance = fabs(contentViewMinX - frame.origin.x);
819 | animationDuration = fabs(distance / velocity.x);
820 | if (animationDuration > ANIMATION_DURATION)
821 | animationDuration = ANIMATION_DURATION;
822 |
823 | frame.origin.x = contentViewMinX;
824 |
825 | if (self.menuWasOpenAtPanBegin)
826 | {
827 | [self.contentViewController viewDidSlideIn:YES inSlideMenuController:self];
828 | [self.contentViewController viewWillSlideOut:YES inSlideMenuController:self];
829 | }
830 |
831 | [UIView animateWithDuration:animationDuration delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
832 | self.contentViewController.view.frame = frame;
833 | } completion:^(BOOL finished) {
834 | self.tapGesture.enabled = YES;
835 |
836 | if (!self.menuWasOpenAtPanBegin)
837 | [self.menuViewController endAppearanceTransition];
838 |
839 | if ([self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)])
840 | [self setNeedsStatusBarAppearanceUpdate];
841 |
842 | [self.contentViewController viewDidSlideOut:YES inSlideMenuController:self];
843 | }];
844 | }
845 |
846 | self.contentViewControllerFrame = frame;
847 | }
848 | }
849 |
850 |
851 | #pragma mark - Menu State
852 |
853 | - (BOOL)isMenuOpen
854 | {
855 | if ([self.contentViewController isViewLoaded])
856 | return self.contentViewController.view.frame.origin.x != 0;
857 |
858 | return NO;
859 | }
860 |
861 |
862 | #pragma mark - Utils
863 |
864 | - (CGFloat)contentViewMinX
865 | {
866 | CGFloat minX = 0;
867 |
868 | if ([self isContentViewHidden])
869 | {
870 | if (self.slideDirection == NVSlideMenuControllerSlideFromLeftToRight)
871 | minX = CGRectGetWidth(self.view.bounds);
872 | else
873 | minX = -CGRectGetWidth(self.view.bounds);
874 | }
875 | else
876 | {
877 | if (self.slideDirection == NVSlideMenuControllerSlideFromLeftToRight)
878 | minX = self.menuWidth;
879 | else
880 | minX = -self.menuWidth;
881 | }
882 |
883 | return minX;
884 | }
885 |
886 |
887 | - (CGRect)menuViewFrameAccordingToCurrentSlideDirection
888 | {
889 | CGRect menuFrame = self.view.bounds;
890 |
891 | if (self.autoAdjustMenuWidth && ![self isContentViewHidden])
892 | menuFrame.size.width = self.menuWidth;
893 |
894 | if (self.autoAdjustMenuWidth && self.slideDirection == NVSlideMenuControllerSlideFromRightToLeft && ![self isContentViewHidden])
895 | menuFrame.origin.x = CGRectGetWidth(self.view.bounds) - self.menuWidth;
896 |
897 | return menuFrame;
898 | }
899 |
900 |
901 | - (UIViewAutoresizing)menuViewAutoresizingMaskAccordingToCurrentSlideDirection
902 | {
903 | UIViewAutoresizing resizingMask = UIViewAutoresizingFlexibleHeight;
904 |
905 | if (self.slideDirection == NVSlideMenuControllerSlideFromLeftToRight)
906 | resizingMask = resizingMask | UIViewAutoresizingFlexibleRightMargin;
907 | else
908 | resizingMask = resizingMask | UIViewAutoresizingFlexibleLeftMargin;
909 |
910 | return resizingMask;
911 | }
912 |
913 | @end
914 |
915 |
916 | #pragma mark -
917 | #pragma mark - UIViewController (NVSlideMenuController)
918 |
919 | @implementation UIViewController (NVSlideMenuController)
920 |
921 | - (NVSlideMenuController *)slideMenuController
922 | {
923 | NVSlideMenuController *slideMenuController = nil;
924 | UIViewController *parentViewController = self.parentViewController;
925 |
926 | while (!slideMenuController && parentViewController)
927 | {
928 | if ([parentViewController isKindOfClass:NVSlideMenuController.class])
929 | slideMenuController = (NVSlideMenuController *) parentViewController;
930 | else
931 | parentViewController = parentViewController.parentViewController;
932 | }
933 |
934 | return slideMenuController;
935 | }
936 |
937 | @end
938 |
939 | #pragma mark - UIViewController (NVSlideMenuControllerCallbacks)
940 |
941 | @implementation UIViewController (NVSlideMenuControllerCallbacks)
942 |
943 | - (void)viewWillSlideIn:(BOOL)animated inSlideMenuController:(NVSlideMenuController *)slideMenuController
944 | {
945 | // default implementation does nothing
946 | }
947 |
948 |
949 | - (void)viewDidSlideIn:(BOOL)animated inSlideMenuController:(NVSlideMenuController *)slideMenuController
950 | {
951 | // default implementation does nothing
952 | }
953 |
954 |
955 | - (void)viewWillSlideOut:(BOOL)animated inSlideMenuController:(NVSlideMenuController *)slideMenuController
956 | {
957 | // default implementation does nothing
958 | }
959 |
960 |
961 | - (void)viewDidSlideOut:(BOOL)animated inSlideMenuController:(NVSlideMenuController *)slideMenuController
962 | {
963 | // default implementation does nothing
964 | }
965 |
966 | @end
967 |
968 |
969 | #pragma mark -
970 | #pragma mark - NVSlideMenuController (Deprecated)
971 |
972 | @implementation NVSlideMenuController (Deprecated)
973 |
974 | - (void)setContentViewWidthWhenMenuIsOpen:(CGFloat)contentViewWidthWhenMenuIsOpen
975 | {
976 | if (contentViewWidthWhenMenuIsOpen != _contentViewWidthWhenMenuIsOpen)
977 | {
978 | _contentViewWidthWhenMenuIsOpen = contentViewWidthWhenMenuIsOpen;
979 | if ([self isViewLoaded])
980 | self.menuWidth = CGRectGetWidth(self.view.bounds) - _contentViewWidthWhenMenuIsOpen;
981 | }
982 | }
983 |
984 |
985 | - (CGFloat)contentViewWidthWhenMenuIsOpen
986 | {
987 | return _contentViewWidthWhenMenuIsOpen;
988 | }
989 |
990 |
991 | - (void)setPanEnabledWhenSlideMenuIsHidden:(BOOL)panEnabledWhenSlideMenuIsHidden
992 | {
993 | self.panGestureEnabled = panEnabledWhenSlideMenuIsHidden;
994 | }
995 |
996 |
997 | - (BOOL)panEnabledWhenSlideMenuIsHidden
998 | {
999 | return self.panGestureEnabled;
1000 | }
1001 |
1002 |
1003 | - (void)setContentViewController:(UIViewController *)contentViewController animated:(BOOL)animated completion:(void(^)(BOOL finished))completion
1004 | {
1005 | [self closeMenuBehindContentViewController:contentViewController animated:animated completion:completion];
1006 | }
1007 |
1008 |
1009 | - (void)showContentViewControllerAnimated:(BOOL)animated completion:(void(^)(BOOL finished))completion
1010 | {
1011 | [self closeMenuAnimated:animated completion:completion];
1012 | }
1013 |
1014 |
1015 | - (void)showMenuAnimated:(BOOL)animated completion:(void(^)(BOOL finished))completion
1016 | {
1017 | [self openMenuAnimated:animated completion:completion];
1018 | }
1019 |
1020 | @end
1021 |
1022 |
1023 |
1024 | #pragma mark -
1025 | #pragma mark Private Categories
1026 |
1027 | #pragma mark UINavigationController (NVSlideMenuControllerCallbacks)
1028 |
1029 | @interface UINavigationController (NVSlideMenuControllerCallbacks)
1030 | // Forward callbacks to the topViewController
1031 | @end
1032 |
1033 | @implementation UINavigationController (NVSlideMenuControllerCallbacks)
1034 |
1035 | - (void)viewWillSlideIn:(BOOL)animated inSlideMenuController:(NVSlideMenuController *)slideMenuController
1036 | {
1037 | [self.topViewController viewWillSlideIn:animated inSlideMenuController:slideMenuController];
1038 | }
1039 |
1040 |
1041 | - (void)viewDidSlideIn:(BOOL)animated inSlideMenuController:(NVSlideMenuController *)slideMenuController
1042 | {
1043 | [self.topViewController viewDidSlideIn:animated inSlideMenuController:slideMenuController];
1044 | }
1045 |
1046 |
1047 | - (void)viewWillSlideOut:(BOOL)animated inSlideMenuController:(NVSlideMenuController *)slideMenuController
1048 | {
1049 | [self.topViewController viewWillSlideOut:animated inSlideMenuController:slideMenuController];
1050 | }
1051 |
1052 |
1053 | - (void)viewDidSlideOut:(BOOL)animated inSlideMenuController:(NVSlideMenuController *)slideMenuController
1054 | {
1055 | [self.topViewController viewDidSlideOut:animated inSlideMenuController:slideMenuController];
1056 | }
1057 |
1058 | @end
1059 |
1060 |
1061 | #pragma mark UISplitViewController (NVSlideMenuControllerCallbacks)
1062 |
1063 | @interface UISplitViewController (NVSlideMenuControllerCallbacks)
1064 | // Forward callbacks to the viewControllers
1065 | @end
1066 |
1067 | @implementation UISplitViewController (NVSlideMenuControllerCallbacks)
1068 |
1069 | - (void)viewWillSlideIn:(BOOL)animated inSlideMenuController:(NVSlideMenuController *)slideMenuController
1070 | {
1071 | [self.viewControllers enumerateObjectsUsingBlock:^(UIViewController *vc, NSUInteger idx, BOOL *stop) {
1072 | [vc viewWillSlideIn:animated inSlideMenuController:slideMenuController];
1073 | }];
1074 | }
1075 |
1076 |
1077 | - (void)viewDidSlideIn:(BOOL)animated inSlideMenuController:(NVSlideMenuController *)slideMenuController
1078 | {
1079 | [self.viewControllers enumerateObjectsUsingBlock:^(UIViewController *vc, NSUInteger idx, BOOL *stop) {
1080 | [vc viewDidSlideIn:animated inSlideMenuController:slideMenuController];
1081 | }];
1082 | }
1083 |
1084 |
1085 | - (void)viewWillSlideOut:(BOOL)animated inSlideMenuController:(NVSlideMenuController *)slideMenuController
1086 | {
1087 | [self.viewControllers enumerateObjectsUsingBlock:^(UIViewController *vc, NSUInteger idx, BOOL *stop) {
1088 | [vc viewWillSlideOut:animated inSlideMenuController:slideMenuController];
1089 | }];
1090 | }
1091 |
1092 |
1093 | - (void)viewDidSlideOut:(BOOL)animated inSlideMenuController:(NVSlideMenuController *)slideMenuController
1094 | {
1095 | [self.viewControllers enumerateObjectsUsingBlock:^(UIViewController *vc, NSUInteger idx, BOOL *stop) {
1096 | [vc viewDidSlideOut:animated inSlideMenuController:slideMenuController];
1097 | }];
1098 | }
1099 |
1100 | @end
1101 |
1102 |
1103 | #pragma mark UITabBarController (NVSlideMenuControllerCallbacks)
1104 |
1105 | @interface UITabBarController (NVSlideMenuControllerCallbacks)
1106 | // Forward callbacks to the selectedViewController
1107 | @end
1108 |
1109 | @implementation UITabBarController (NVSlideMenuControllerCallbacks)
1110 |
1111 | - (void)viewWillSlideIn:(BOOL)animated inSlideMenuController:(NVSlideMenuController *)slideMenuController
1112 | {
1113 | [self.selectedViewController viewWillSlideIn:animated inSlideMenuController:slideMenuController];
1114 | }
1115 |
1116 |
1117 | - (void)viewDidSlideIn:(BOOL)animated inSlideMenuController:(NVSlideMenuController *)slideMenuController
1118 | {
1119 | [self.selectedViewController viewDidSlideIn:animated inSlideMenuController:slideMenuController];
1120 | }
1121 |
1122 |
1123 | - (void)viewWillSlideOut:(BOOL)animated inSlideMenuController:(NVSlideMenuController *)slideMenuController
1124 | {
1125 | [self.selectedViewController viewWillSlideOut:animated inSlideMenuController:slideMenuController];
1126 | }
1127 |
1128 |
1129 | - (void)viewDidSlideOut:(BOOL)animated inSlideMenuController:(NVSlideMenuController *)slideMenuController
1130 | {
1131 | [self.selectedViewController viewDidSlideOut:animated inSlideMenuController:slideMenuController];
1132 | }
1133 |
1134 | @end
1135 |
1136 |
1137 | #pragma mark UIPageViewController (NVSlideMenuControllerCallbacks)
1138 |
1139 | @interface UIPageViewController (NVSlideMenuControllerCallbacks)
1140 | // Forward callbacks to the viewControllers
1141 | @end
1142 |
1143 | @implementation UIPageViewController (NVSlideMenuControllerCallbacks)
1144 |
1145 | - (void)viewWillSlideIn:(BOOL)animated inSlideMenuController:(NVSlideMenuController *)slideMenuController
1146 | {
1147 | [self.viewControllers enumerateObjectsUsingBlock:^(UIViewController *vc, NSUInteger idx, BOOL *stop) {
1148 | [vc viewWillSlideIn:animated inSlideMenuController:slideMenuController];
1149 | }];
1150 | }
1151 |
1152 |
1153 | - (void)viewDidSlideIn:(BOOL)animated inSlideMenuController:(NVSlideMenuController *)slideMenuController
1154 | {
1155 | [self.viewControllers enumerateObjectsUsingBlock:^(UIViewController *vc, NSUInteger idx, BOOL *stop) {
1156 | [vc viewDidSlideIn:animated inSlideMenuController:slideMenuController];
1157 | }];
1158 | }
1159 |
1160 |
1161 | - (void)viewWillSlideOut:(BOOL)animated inSlideMenuController:(NVSlideMenuController *)slideMenuController
1162 | {
1163 | [self.viewControllers enumerateObjectsUsingBlock:^(UIViewController *vc, NSUInteger idx, BOOL *stop) {
1164 | [vc viewWillSlideOut:animated inSlideMenuController:slideMenuController];
1165 | }];
1166 | }
1167 |
1168 |
1169 | - (void)viewDidSlideOut:(BOOL)animated inSlideMenuController:(NVSlideMenuController *)slideMenuController
1170 | {
1171 | [self.viewControllers enumerateObjectsUsingBlock:^(UIViewController *vc, NSUInteger idx, BOOL *stop) {
1172 | [vc viewDidSlideOut:animated inSlideMenuController:slideMenuController];
1173 | }];
1174 | }
1175 |
1176 | @end
1177 |
--------------------------------------------------------------------------------