react-native-navigation 7.45.0 → 7.47.0-snapshot.1697
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/Mock/Components/ComponentScreen.tsx +2 -1
- package/lib/Mock/Components/LayoutComponent.tsx +9 -0
- package/lib/Mock/Components/SideMenu.tsx +27 -0
- package/lib/Mock/Layouts/BottomTabsNode.ts +4 -2
- package/lib/Mock/Layouts/LayoutNodeFactory.ts +14 -1
- package/lib/Mock/Layouts/ParentNode.ts +4 -0
- package/lib/Mock/Layouts/SideMenu.ts +87 -0
- package/lib/Mock/Stores/LayoutStore.ts +42 -10
- package/lib/Mock/actions/layoutActions.ts +11 -0
- package/lib/Mock/index.js +2 -2
- package/lib/Mock/mocks/NativeCommandsSender.tsx +1 -0
- package/lib/android/app/build.gradle +0 -1
- package/lib/android/app/src/main/java/com/reactnativenavigation/NavigationApplication.java +3 -9
- package/lib/android/app/src/main/java/com/reactnativenavigation/react/ReactView.java +3 -3
- package/lib/android/app/src/main/java/com/reactnativenavigation/react/modal/ModalHostLayout.kt +1 -1
- package/lib/android/app/src/main/java/com/reactnativenavigation/utils/ReactTypefaceUtils.java +2 -3
- package/lib/android/app/src/main/java/com/reactnativenavigation/utils/ReactViewGroup.kt +2 -4
- package/lib/android/app/src/main/java/com/reactnativenavigation/utils/ViewUtils.java +4 -6
- package/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/LayoutDirectionApplier.kt +4 -8
- package/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/RootPresenter.java +1 -1
- package/lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/BackgroundColorAnimator.kt +4 -6
- package/lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/BackgroundColorEvaluator.kt +2 -4
- package/lib/android/app/src/reactNative71/java/com/reactnativenavigation/react/modal/ModalContentLayout.kt +6 -6
- package/lib/dist/Mock/Application.d.ts +6 -4
- package/lib/dist/Mock/Components/BottomTabs.d.ts +13 -9
- package/lib/dist/Mock/Components/ComponentScreen.d.ts +7 -5
- package/lib/dist/Mock/Components/ComponentScreen.js +2 -1
- package/lib/dist/Mock/Components/LayoutComponent.d.ts +13 -9
- package/lib/dist/Mock/Components/LayoutComponent.js +9 -0
- package/lib/dist/Mock/Components/Modals.d.ts +13 -9
- package/lib/dist/Mock/Components/NavigationButton.d.ts +15 -11
- package/lib/dist/Mock/Components/Overlays.d.ts +13 -9
- package/lib/dist/Mock/Components/SideMenu.d.ts +62 -0
- package/lib/dist/Mock/Components/SideMenu.js +25 -0
- package/lib/dist/Mock/Components/Stack.d.ts +13 -9
- package/lib/dist/Mock/Components/TopBar.d.ts +9 -7
- package/lib/dist/Mock/Layouts/BottomTabsNode.d.ts +1 -1
- package/lib/dist/Mock/Layouts/BottomTabsNode.js +3 -2
- package/lib/dist/Mock/Layouts/LayoutNodeFactory.d.ts +2 -1
- package/lib/dist/Mock/Layouts/LayoutNodeFactory.js +10 -1
- package/lib/dist/Mock/Layouts/ParentNode.d.ts +1 -0
- package/lib/dist/Mock/Layouts/ParentNode.js +3 -0
- package/lib/dist/Mock/Layouts/SideMenu.d.ts +31 -0
- package/lib/dist/Mock/Layouts/SideMenu.js +80 -0
- package/lib/dist/Mock/Stores/LayoutStore.js +38 -9
- package/lib/dist/Mock/actions/layoutActions.d.ts +3 -0
- package/lib/dist/Mock/actions/layoutActions.js +11 -1
- package/lib/dist/Mock/connect.js +1 -2
- package/lib/dist/Mock/index.js +2 -2
- package/lib/dist/Mock/mocks/NativeCommandsSender.js +1 -0
- package/lib/dist/src/adapters/NativeEventsReceiver.js +1 -1
- package/lib/dist/src/adapters/TouchablePreview.d.ts +2 -2
- package/lib/dist/src/commands/LayoutType.js +1 -1
- package/lib/dist/src/components/Modal.d.ts +1 -1
- package/lib/dist/src/interfaces/CommandName.js +1 -1
- package/lib/dist/src/interfaces/Options.d.ts +9 -0
- package/lib/dist/src/interfaces/Options.js +2 -2
- package/lib/dist/src/types.d.ts +0 -1
- package/lib/ios/BottomTabsBasePresenter.m +2 -3
- package/lib/ios/RNNAppDelegate.mm +3 -2
- package/lib/ios/RNNConvert.h +0 -2
- package/lib/ios/RNNConvert.m +0 -4
- package/lib/ios/RNNSideMenu/MMDrawerController/MMDrawerController.h +22 -0
- package/lib/ios/RNNSideMenu/MMDrawerController/MMDrawerController.m +813 -159
- package/lib/ios/RNNSideMenuPresenter.m +27 -0
- package/lib/ios/RNNSideMenuSideOptions.h +6 -0
- package/lib/ios/RNNSideMenuSideOptions.m +13 -1
- package/lib/ios/RNNStackPresenter.m +2 -2
- package/lib/src/adapters/NativeEventsReceiver.ts +3 -3
- package/lib/src/adapters/TouchablePreview.tsx +3 -3
- package/lib/src/interfaces/Options.ts +9 -0
- package/package.json +31 -37
|
@@ -152,6 +152,8 @@ static NSString *MMDrawerOpenSideKey = @"MMDrawerOpenSide";
|
|
|
152
152
|
CGFloat _maximumRightDrawerWidth;
|
|
153
153
|
CGFloat _maximumLeftDrawerWidth;
|
|
154
154
|
UIColor *_statusBarViewBackgroundColor;
|
|
155
|
+
MMDrawerOpenMode _leftDrawerOpenMode;
|
|
156
|
+
MMDrawerOpenMode _rightDrawerOpenMode;
|
|
155
157
|
}
|
|
156
158
|
|
|
157
159
|
@property(nonatomic, assign, readwrite) MMDrawerSide openSide;
|
|
@@ -167,6 +169,9 @@ static NSString *MMDrawerOpenSideKey = @"MMDrawerOpenSide";
|
|
|
167
169
|
@property(nonatomic, copy) MMDrawerGestureCompletionBlock gestureCompletion;
|
|
168
170
|
@property(nonatomic, assign, getter=isAnimatingDrawer) BOOL animatingDrawer;
|
|
169
171
|
@property(nonatomic, strong) UIGestureRecognizer *pan;
|
|
172
|
+
@property (nonatomic, strong) UIView *centerContentOverlay;
|
|
173
|
+
@property (nonatomic, assign) MMDrawerSide startingDrawerSide;
|
|
174
|
+
|
|
170
175
|
|
|
171
176
|
@end
|
|
172
177
|
|
|
@@ -227,6 +232,8 @@ static NSString *MMDrawerOpenSideKey = @"MMDrawerOpenSide";
|
|
|
227
232
|
[self setShowsShadow:YES];
|
|
228
233
|
[self setShouldStretchLeftDrawer:YES];
|
|
229
234
|
[self setShouldStretchRightDrawer:YES];
|
|
235
|
+
[self side:MMDrawerSideRight openMode:MMDrawerOpenModePushContent];
|
|
236
|
+
[self side:MMDrawerSideLeft openMode:MMDrawerOpenModePushContent];
|
|
230
237
|
|
|
231
238
|
[self setOpenDrawerGestureModeMask:MMOpenDrawerGestureModeNone];
|
|
232
239
|
[self setCloseDrawerGestureModeMask:MMCloseDrawerGestureModeNone];
|
|
@@ -323,52 +330,126 @@ static NSString *MMDrawerOpenSideKey = @"MMDrawerOpenSide";
|
|
|
323
330
|
}
|
|
324
331
|
} else {
|
|
325
332
|
[self setAnimatingDrawer:animated];
|
|
326
|
-
|
|
333
|
+
MMDrawerSide visibleSide = self.openSide;
|
|
327
334
|
|
|
328
|
-
|
|
329
|
-
|
|
335
|
+
if (visibleSide == MMDrawerSideNone) {
|
|
336
|
+
[self setAnimatingDrawer:NO];
|
|
337
|
+
if (completion) {
|
|
338
|
+
completion(NO);
|
|
339
|
+
}
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
330
342
|
|
|
331
|
-
|
|
332
|
-
|
|
343
|
+
UIViewController *sideDrawerViewController = [self sideDrawerViewControllerForSide:visibleSide];
|
|
344
|
+
[sideDrawerViewController beginAppearanceTransition:NO animated:animated];
|
|
333
345
|
|
|
334
|
-
|
|
335
|
-
CGFloat percentVisble = 0.0;
|
|
346
|
+
MMDrawerOpenMode openMode = [self drawerOpenModeForSide:visibleSide];
|
|
336
347
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
348
|
+
if (openMode == MMDrawerOpenModeAboveContent) {
|
|
349
|
+
[self closeDrawerInOverlayMode:sideDrawerViewController
|
|
350
|
+
visibleSide:visibleSide
|
|
351
|
+
animated:animated
|
|
352
|
+
velocity:velocity
|
|
353
|
+
animationOptions:options
|
|
354
|
+
completion:completion];
|
|
355
|
+
} else {
|
|
356
|
+
[self closeDrawerInPushMode:sideDrawerViewController
|
|
357
|
+
visibleSide:visibleSide
|
|
358
|
+
animated:animated
|
|
359
|
+
velocity:velocity
|
|
360
|
+
animationOptions:options
|
|
361
|
+
completion:completion];
|
|
346
362
|
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
347
365
|
|
|
348
|
-
|
|
349
|
-
|
|
366
|
+
- (void)closeDrawerInOverlayMode:(UIViewController *)sideDrawerViewController
|
|
367
|
+
visibleSide:(MMDrawerSide)visibleSide
|
|
368
|
+
animated:(BOOL)animated
|
|
369
|
+
velocity:(CGFloat)velocity
|
|
370
|
+
animationOptions:(UIViewAnimationOptions)options
|
|
371
|
+
completion:(void (^)(BOOL finished))completion {
|
|
372
|
+
CGFloat maximumDrawerWidth = [self maximumDrawerWidthForSide:visibleSide];
|
|
350
373
|
|
|
351
|
-
|
|
374
|
+
CGRect currentFrame = sideDrawerViewController.view.frame;
|
|
375
|
+
CGRect finalFrame = currentFrame;
|
|
352
376
|
|
|
353
|
-
|
|
377
|
+
// Set final position based on side
|
|
378
|
+
if (visibleSide == MMDrawerSideLeft) {
|
|
379
|
+
finalFrame.origin.x = -maximumDrawerWidth; // Off-screen left
|
|
380
|
+
} else { // MMDrawerSideRight
|
|
381
|
+
finalFrame.origin.x = self.view.bounds.size.width; // Off-screen right
|
|
382
|
+
}
|
|
354
383
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
384
|
+
// Ensure overlay is in view hierarchy
|
|
385
|
+
if (self.centerContentOverlay && self.centerContentOverlay.superview == nil) {
|
|
386
|
+
[self.centerContainerView addSubview:self.centerContentOverlay];
|
|
387
|
+
[self.centerContainerView bringSubviewToFront:self.centerContentOverlay];
|
|
388
|
+
self.centerContentOverlay.alpha = 0.5;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
CGFloat distance = ABS(currentFrame.origin.x - finalFrame.origin.x);
|
|
392
|
+
NSTimeInterval duration = [self animationDurationForDistance:distance velocity:velocity];
|
|
393
|
+
|
|
394
|
+
[UIView animateWithDuration:(animated ? duration : 0.0)
|
|
395
|
+
delay:0.0
|
|
396
|
+
options:options
|
|
397
|
+
animations:^{
|
|
398
|
+
[self setNeedsStatusBarAppearanceUpdateIfSupported];
|
|
399
|
+
[sideDrawerViewController.view setFrame:finalFrame];
|
|
400
|
+
self.centerContentOverlay.alpha = 0.0;
|
|
401
|
+
[self updateDrawerVisualStateForDrawerSide:visibleSide percentVisible:0.0];
|
|
402
|
+
}
|
|
403
|
+
completion:^(BOOL finished) {
|
|
404
|
+
[self completeDrawerClosingForSide:visibleSide
|
|
405
|
+
sideDrawerViewController:sideDrawerViewController
|
|
406
|
+
finished:finished
|
|
407
|
+
completion:completion];
|
|
408
|
+
[self.centerContentOverlay removeFromSuperview];
|
|
409
|
+
}];
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
- (void)closeDrawerInPushMode:(UIViewController *)sideDrawerViewController
|
|
413
|
+
visibleSide:(MMDrawerSide)visibleSide
|
|
414
|
+
animated:(BOOL)animated
|
|
415
|
+
velocity:(CGFloat)velocity
|
|
416
|
+
animationOptions:(UIViewAnimationOptions)options
|
|
417
|
+
completion:(void (^)(BOOL finished))completion {
|
|
418
|
+
|
|
419
|
+
CGRect newFrame = self.childControllerContainerView.bounds;
|
|
420
|
+
|
|
421
|
+
CGFloat distance = ABS(CGRectGetMinX(self.centerContainerView.frame));
|
|
422
|
+
NSTimeInterval duration = [self animationDurationForDistance:distance velocity:velocity];
|
|
423
|
+
sideDrawerViewController.view.hidden = NO;
|
|
424
|
+
|
|
425
|
+
[UIView animateWithDuration:(animated ? duration : 0.0)
|
|
426
|
+
delay:0.0
|
|
427
|
+
options:options
|
|
428
|
+
animations:^{
|
|
429
|
+
[self setNeedsStatusBarAppearanceUpdateIfSupported];
|
|
430
|
+
[self.centerContainerView setFrame:newFrame];
|
|
431
|
+
[self updateDrawerVisualStateForDrawerSide:visibleSide percentVisible:0.0];
|
|
432
|
+
}
|
|
433
|
+
completion:^(BOOL finished) {
|
|
434
|
+
[self completeDrawerClosingForSide:visibleSide
|
|
435
|
+
sideDrawerViewController:sideDrawerViewController
|
|
436
|
+
finished:finished
|
|
437
|
+
completion:completion];
|
|
438
|
+
}];
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
- (void)completeDrawerClosingForSide:(MMDrawerSide)visibleSide
|
|
442
|
+
sideDrawerViewController:(UIViewController *)sideDrawerViewController
|
|
443
|
+
finished:(BOOL)finished
|
|
444
|
+
completion:(void (^)(BOOL finished))completion {
|
|
445
|
+
[sideDrawerViewController endAppearanceTransition];
|
|
446
|
+
|
|
447
|
+
[self setOpenSide:MMDrawerSideNone];
|
|
448
|
+
[self resetDrawerVisualStateForDrawerSide:visibleSide];
|
|
449
|
+
[self setAnimatingDrawer:NO];
|
|
450
|
+
|
|
451
|
+
if (completion) {
|
|
452
|
+
completion(finished);
|
|
372
453
|
}
|
|
373
454
|
}
|
|
374
455
|
|
|
@@ -397,49 +478,206 @@ static NSString *MMDrawerOpenSideKey = @"MMDrawerOpenSide";
|
|
|
397
478
|
}
|
|
398
479
|
} else {
|
|
399
480
|
[self setAnimatingDrawer:animated];
|
|
400
|
-
UIViewController *sideDrawerViewController =
|
|
401
|
-
|
|
481
|
+
UIViewController *sideDrawerViewController = [self sideDrawerViewControllerForSide:drawerSide];
|
|
482
|
+
|
|
402
483
|
if (self.openSide != drawerSide) {
|
|
403
484
|
[self prepareToPresentDrawer:drawerSide animated:animated];
|
|
404
485
|
}
|
|
405
486
|
|
|
406
487
|
if (sideDrawerViewController) {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
if (
|
|
410
|
-
|
|
411
|
-
|
|
488
|
+
MMDrawerOpenMode openMode = [self drawerOpenModeForSide:drawerSide];
|
|
489
|
+
|
|
490
|
+
if (openMode == MMDrawerOpenModeAboveContent) {
|
|
491
|
+
[self openDrawerInOverlayMode:sideDrawerViewController
|
|
492
|
+
drawerSide:drawerSide
|
|
493
|
+
animated:animated
|
|
494
|
+
velocity:velocity
|
|
495
|
+
animationOptions:options
|
|
496
|
+
completion:completion];
|
|
412
497
|
} else {
|
|
413
|
-
|
|
414
|
-
|
|
498
|
+
[self openDrawerInPushMode:sideDrawerViewController
|
|
499
|
+
drawerSide:drawerSide
|
|
500
|
+
animated:animated
|
|
501
|
+
velocity:velocity
|
|
502
|
+
animationOptions:options
|
|
503
|
+
completion:completion];
|
|
415
504
|
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
416
508
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
options:options
|
|
424
|
-
animations:^{
|
|
425
|
-
[self setNeedsStatusBarAppearanceUpdateIfSupported];
|
|
426
|
-
[self.centerContainerView setFrame:newFrame withLayoutAlpha:1.0];
|
|
427
|
-
[self updateDrawerVisualStateForDrawerSide:drawerSide percentVisible:1.0];
|
|
428
|
-
}
|
|
429
|
-
completion:^(BOOL finished) {
|
|
430
|
-
// End the appearance transition if it already wasn't open.
|
|
431
|
-
if (drawerSide != self.openSide) {
|
|
432
|
-
[sideDrawerViewController endAppearanceTransition];
|
|
433
|
-
}
|
|
434
|
-
[self setOpenSide:drawerSide];
|
|
509
|
+
- (void)openDrawerInOverlayMode:(UIViewController *)sideDrawerViewController
|
|
510
|
+
drawerSide:(MMDrawerSide)drawerSide
|
|
511
|
+
animated:(BOOL)animated
|
|
512
|
+
velocity:(CGFloat)velocity
|
|
513
|
+
animationOptions:(UIViewAnimationOptions)options
|
|
514
|
+
completion:(void (^)(BOOL finished))completion {
|
|
435
515
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
516
|
+
CGFloat maximumDrawerWidth = [self maximumDrawerWidthForSide:drawerSide];
|
|
517
|
+
|
|
518
|
+
CGRect drawerFrame = sideDrawerViewController.view.frame;
|
|
519
|
+
CGRect initialFrame = sideDrawerViewController.view.frame;
|
|
520
|
+
|
|
521
|
+
drawerFrame.size.width = maximumDrawerWidth;
|
|
522
|
+
initialFrame.size.width = maximumDrawerWidth;
|
|
523
|
+
|
|
524
|
+
if (drawerSide == MMDrawerSideLeft) {
|
|
525
|
+
drawerFrame.origin.x = 0; // Final position
|
|
526
|
+
|
|
527
|
+
if (self.openSide != drawerSide) {
|
|
528
|
+
initialFrame.origin.x = -maximumDrawerWidth; // Start off-screen
|
|
529
|
+
[sideDrawerViewController.view setFrame:initialFrame];
|
|
530
|
+
}
|
|
531
|
+
} else { // MMDrawerSideRight
|
|
532
|
+
CGFloat screenWidth = self.view.bounds.size.width;
|
|
533
|
+
drawerFrame.origin.x = screenWidth - maximumDrawerWidth; // Final position
|
|
534
|
+
|
|
535
|
+
if (self.openSide != drawerSide) {
|
|
536
|
+
initialFrame.origin.x = screenWidth; // Start off-screen
|
|
537
|
+
[sideDrawerViewController.view setFrame:initialFrame];
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
[self setupCenterContentOverlay];
|
|
542
|
+
[self.centerContainerView addSubview:self.centerContentOverlay];
|
|
543
|
+
[self.centerContainerView bringSubviewToFront:self.centerContentOverlay];
|
|
544
|
+
self.centerContentOverlay.alpha = 0.0; // Start transparent
|
|
545
|
+
|
|
546
|
+
// Make sure drawer is visible and in front
|
|
547
|
+
sideDrawerViewController.view.hidden = NO;
|
|
548
|
+
[self.childControllerContainerView bringSubviewToFront:sideDrawerViewController.view];
|
|
549
|
+
|
|
550
|
+
CGFloat distance = ABS(initialFrame.origin.x - drawerFrame.origin.x);
|
|
551
|
+
NSTimeInterval duration = [self animationDurationForDistance:distance velocity:velocity];
|
|
552
|
+
|
|
553
|
+
[UIView animateWithDuration:(animated ? duration : 0.0)
|
|
554
|
+
delay:0.0
|
|
555
|
+
options:options
|
|
556
|
+
animations:^{
|
|
557
|
+
[self setNeedsStatusBarAppearanceUpdateIfSupported];
|
|
558
|
+
[sideDrawerViewController.view setFrame:drawerFrame]; // Move to final position
|
|
559
|
+
self.centerContentOverlay.alpha = 0.5;
|
|
560
|
+
[self updateDrawerVisualStateForDrawerSide:drawerSide percentVisible:1.0];
|
|
561
|
+
}
|
|
562
|
+
completion:^(BOOL finished) {
|
|
563
|
+
[self completeDrawerOpeningForSide:drawerSide
|
|
564
|
+
sideDrawerViewController:sideDrawerViewController
|
|
565
|
+
finished:finished
|
|
566
|
+
completion:completion];
|
|
567
|
+
}];
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
- (void)openDrawerInPushMode:(UIViewController *)sideDrawerViewController
|
|
571
|
+
drawerSide:(MMDrawerSide)drawerSide
|
|
572
|
+
animated:(BOOL)animated
|
|
573
|
+
velocity:(CGFloat)velocity
|
|
574
|
+
animationOptions:(UIViewAnimationOptions)options
|
|
575
|
+
completion:(void (^)(BOOL finished))completion {
|
|
576
|
+
|
|
577
|
+
CGRect oldFrame = self.centerContainerView.frame;
|
|
578
|
+
CGRect newFrame = self.centerContainerView.frame;
|
|
579
|
+
|
|
580
|
+
if (drawerSide == MMDrawerSideLeft) {
|
|
581
|
+
newFrame.origin.x = self.maximumLeftDrawerWidth;
|
|
582
|
+
} else {
|
|
583
|
+
newFrame.origin.x = 0 - self.maximumRightDrawerWidth;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
CGFloat distance = ABS(CGRectGetMinX(oldFrame) - newFrame.origin.x);
|
|
587
|
+
NSTimeInterval duration = MAX(distance / ABS(velocity), MMDrawerMinimumAnimationDuration);
|
|
588
|
+
BOOL isGestureOpen = !CGRectIsNull(self.startingPanRect) && [self shouldStretchForSide:drawerSide];
|
|
589
|
+
sideDrawerViewController.view.hidden = NO;
|
|
590
|
+
|
|
591
|
+
[UIView animateWithDuration:(animated ? duration : 0.0)
|
|
592
|
+
delay:0.0
|
|
593
|
+
options:options
|
|
594
|
+
animations:^{
|
|
595
|
+
[self setNeedsStatusBarAppearanceUpdateIfSupported];
|
|
596
|
+
[self.centerContainerView setFrame:newFrame withLayoutAlpha:1.0];
|
|
597
|
+
[self updateDrawerVisualStateForDrawerSide:drawerSide percentVisible:1.0];
|
|
442
598
|
}
|
|
599
|
+
completion:^(BOOL finished) {
|
|
600
|
+
[self completeDrawerOpeningForSide:drawerSide
|
|
601
|
+
sideDrawerViewController:sideDrawerViewController
|
|
602
|
+
finished:finished
|
|
603
|
+
completion:^(BOOL innerFinished) {
|
|
604
|
+
|
|
605
|
+
if (isGestureOpen) {
|
|
606
|
+
[self applyBounceForDrawerSide:drawerSide];
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
if (completion) {
|
|
610
|
+
completion(innerFinished);
|
|
611
|
+
}
|
|
612
|
+
}];
|
|
613
|
+
}];
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
- (void)applyBounceForDrawerSide:(MMDrawerSide)drawerSide {
|
|
617
|
+
UIViewController *sideDrawerViewController = [self sideDrawerViewControllerForSide:drawerSide];
|
|
618
|
+
if (!sideDrawerViewController) return;
|
|
619
|
+
|
|
620
|
+
CGRect centerFrame = self.centerContainerView.frame;
|
|
621
|
+
CGRect bounceFrame = centerFrame;
|
|
622
|
+
CGFloat bounceAmount = 15.0;
|
|
623
|
+
|
|
624
|
+
if (drawerSide == MMDrawerSideLeft) {
|
|
625
|
+
bounceFrame.origin.x += bounceAmount;
|
|
626
|
+
} else {
|
|
627
|
+
bounceFrame.origin.x -= bounceAmount;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
CATransform3D originalTransform = sideDrawerViewController.view.layer.transform;
|
|
631
|
+
CGFloat scale = 1.0 + (bounceAmount / (drawerSide == MMDrawerSideLeft ?
|
|
632
|
+
self.maximumLeftDrawerWidth :
|
|
633
|
+
self.maximumRightDrawerWidth));
|
|
634
|
+
|
|
635
|
+
CATransform3D bounceTransform = CATransform3DMakeScale(scale, 1.0, 1.0);
|
|
636
|
+
|
|
637
|
+
if (drawerSide == MMDrawerSideLeft) {
|
|
638
|
+
bounceTransform = CATransform3DTranslate(bounceTransform,
|
|
639
|
+
self.maximumLeftDrawerWidth * (scale - 1.0) / 2.0,
|
|
640
|
+
0, 0);
|
|
641
|
+
} else {
|
|
642
|
+
bounceTransform = CATransform3DTranslate(bounceTransform,
|
|
643
|
+
-self.maximumRightDrawerWidth * (scale - 1.0) / 2.0,
|
|
644
|
+
0, 0);
|
|
645
|
+
}
|
|
646
|
+
[UIView animateWithDuration:0.15
|
|
647
|
+
delay:0.0
|
|
648
|
+
options:UIViewAnimationOptionCurveEaseOut
|
|
649
|
+
animations:^{
|
|
650
|
+
self.centerContainerView.frame = bounceFrame;
|
|
651
|
+
sideDrawerViewController.view.layer.transform = bounceTransform;
|
|
652
|
+
}
|
|
653
|
+
completion:^(BOOL bounceFinished) {
|
|
654
|
+
[UIView animateWithDuration:0.15
|
|
655
|
+
delay:0.0
|
|
656
|
+
options:UIViewAnimationOptionCurveEaseIn
|
|
657
|
+
animations:^{
|
|
658
|
+
self.centerContainerView.frame = centerFrame;
|
|
659
|
+
sideDrawerViewController.view.layer.transform = originalTransform;
|
|
660
|
+
}
|
|
661
|
+
completion:nil];
|
|
662
|
+
}];
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
- (void)completeDrawerOpeningForSide:(MMDrawerSide)drawerSide
|
|
666
|
+
sideDrawerViewController:(UIViewController *)sideDrawerViewController
|
|
667
|
+
finished:(BOOL)finished
|
|
668
|
+
completion:(void (^)(BOOL finished))completion {
|
|
669
|
+
|
|
670
|
+
// End the appearance transition if it already wasn't open.
|
|
671
|
+
if (drawerSide != self.openSide) {
|
|
672
|
+
[sideDrawerViewController endAppearanceTransition];
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
[self setOpenSide:drawerSide];
|
|
676
|
+
[self resetDrawerVisualStateForDrawerSide:drawerSide];
|
|
677
|
+
[self setAnimatingDrawer:NO];
|
|
678
|
+
|
|
679
|
+
if (completion) {
|
|
680
|
+
completion(finished);
|
|
443
681
|
}
|
|
444
682
|
}
|
|
445
683
|
|
|
@@ -801,7 +1039,7 @@ static NSString *MMDrawerOpenSideKey = @"MMDrawerOpenSide";
|
|
|
801
1039
|
[super viewDidLoad];
|
|
802
1040
|
|
|
803
1041
|
[self.view setBackgroundColor:[UIColor blackColor]];
|
|
804
|
-
|
|
1042
|
+
[self.view setAccessibilityIdentifier:@"SideMenuContainer"];
|
|
805
1043
|
[self setupGestureRecognizers];
|
|
806
1044
|
}
|
|
807
1045
|
|
|
@@ -1070,6 +1308,16 @@ static NSString *MMDrawerOpenSideKey = @"MMDrawerOpenSide";
|
|
|
1070
1308
|
[self.dummyStatusBarView setBackgroundColor:_statusBarViewBackgroundColor];
|
|
1071
1309
|
}
|
|
1072
1310
|
|
|
1311
|
+
- (void)setLeftDrawerOpenMode:(MMDrawerOpenMode)openMode {
|
|
1312
|
+
if (self.openSide == MMDrawerSideLeft) return;
|
|
1313
|
+
_leftDrawerOpenMode = openMode;
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
- (void)setRightDrawerOpenMode:(MMDrawerOpenMode)openMode {
|
|
1317
|
+
if (self.openSide == MMDrawerSideRight) return;
|
|
1318
|
+
_rightDrawerOpenMode = openMode;
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1073
1321
|
- (void)setAnimatingDrawer:(BOOL)animatingDrawer {
|
|
1074
1322
|
_animatingDrawer = animatingDrawer;
|
|
1075
1323
|
[self.view setUserInteractionEnabled:!animatingDrawer];
|
|
@@ -1085,6 +1333,7 @@ static NSString *MMDrawerOpenSideKey = @"MMDrawerOpenSide";
|
|
|
1085
1333
|
[self updatePanHandlersState];
|
|
1086
1334
|
}
|
|
1087
1335
|
|
|
1336
|
+
|
|
1088
1337
|
#pragma mark - Getters
|
|
1089
1338
|
- (CGFloat)maximumLeftDrawerWidth {
|
|
1090
1339
|
if (self.leftDrawerViewController) {
|
|
@@ -1155,107 +1404,452 @@ static NSString *MMDrawerOpenSideKey = @"MMDrawerOpenSide";
|
|
|
1155
1404
|
return _statusBarViewBackgroundColor;
|
|
1156
1405
|
}
|
|
1157
1406
|
|
|
1407
|
+
- (MMDrawerOpenMode)rightDrawerOpenMode {
|
|
1408
|
+
return _rightDrawerOpenMode;
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
- (MMDrawerOpenMode)leftDrawerOpenMode {
|
|
1412
|
+
return _leftDrawerOpenMode;
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1158
1415
|
#pragma mark - Gesture Handlers
|
|
1159
1416
|
|
|
1160
1417
|
- (void)tapGestureCallback:(UITapGestureRecognizer *)tapGesture {
|
|
1161
1418
|
if (self.openSide != MMDrawerSideNone && self.isAnimatingDrawer == NO) {
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1419
|
+
// Get the tap location
|
|
1420
|
+
CGPoint tapLocation = [tapGesture locationInView:self.childControllerContainerView];
|
|
1421
|
+
|
|
1422
|
+
// Get the open drawer view controller
|
|
1423
|
+
UIViewController *sideDrawerViewController = [self sideDrawerViewControllerForSide:self.openSide];
|
|
1424
|
+
|
|
1425
|
+
// Check if we are in above content mode
|
|
1426
|
+
MMDrawerOpenMode openMode = [self drawerOpenModeForSide:self.openSide];
|
|
1427
|
+
|
|
1428
|
+
// Only close if not tapping on the drawer itself in above content mode
|
|
1429
|
+
BOOL shouldClose = YES;
|
|
1430
|
+
|
|
1431
|
+
if (openMode == MMDrawerOpenModeAboveContent && sideDrawerViewController) {
|
|
1432
|
+
// Check if tap is inside the drawer view
|
|
1433
|
+
CGPoint tapInDrawerView = [tapGesture locationInView:sideDrawerViewController.view];
|
|
1434
|
+
if (CGRectContainsPoint(sideDrawerViewController.view.bounds, tapInDrawerView)) {
|
|
1435
|
+
shouldClose = NO;
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
if (shouldClose) {
|
|
1440
|
+
[self closeDrawerAnimated:YES
|
|
1441
|
+
completion:^(BOOL finished) {
|
|
1442
|
+
if (self.gestureCompletion) {
|
|
1443
|
+
self.gestureCompletion(self, tapGesture);
|
|
1444
|
+
}
|
|
1445
|
+
}];
|
|
1446
|
+
}
|
|
1168
1447
|
}
|
|
1169
1448
|
}
|
|
1170
1449
|
|
|
1171
1450
|
- (void)panGestureCallback:(UIPanGestureRecognizer *)panGesture {
|
|
1172
1451
|
switch (panGesture.state) {
|
|
1173
|
-
case UIGestureRecognizerStateBegan:
|
|
1174
|
-
|
|
1175
|
-
|
|
1452
|
+
case UIGestureRecognizerStateBegan:
|
|
1453
|
+
[self handlePanGestureBegan:panGesture];
|
|
1454
|
+
break;
|
|
1455
|
+
case UIGestureRecognizerStateChanged:
|
|
1456
|
+
[self handlePanGestureChanged:panGesture];
|
|
1457
|
+
break;
|
|
1458
|
+
case UIGestureRecognizerStateEnded:
|
|
1459
|
+
case UIGestureRecognizerStateCancelled:
|
|
1460
|
+
[self handlePanGestureEnded:panGesture];
|
|
1461
|
+
break;
|
|
1462
|
+
default:
|
|
1463
|
+
break;
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
- (CGRect)calculateClosedDrawerPanStartFrameInOverlay:(MMDrawerSide)drawerSide
|
|
1468
|
+
drawerViewController:(UIViewController *)drawerViewController {
|
|
1469
|
+
CGRect drawerFrame = drawerViewController.view.frame;
|
|
1470
|
+
CGFloat maximumDrawerWidth = [self maximumDrawerWidthForSide:drawerSide];
|
|
1471
|
+
|
|
1472
|
+
drawerFrame.size.width = maximumDrawerWidth;
|
|
1473
|
+
|
|
1474
|
+
// Position off-screen based on side
|
|
1475
|
+
if (drawerSide == MMDrawerSideLeft) {
|
|
1476
|
+
drawerFrame.origin.x = -maximumDrawerWidth;
|
|
1477
|
+
} else {
|
|
1478
|
+
drawerFrame.origin.x = self.view.bounds.size.width;
|
|
1479
|
+
}
|
|
1480
|
+
return drawerFrame;
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
- (void)handlePanGestureBegan:(UIPanGestureRecognizer *)panGesture {
|
|
1484
|
+
if (self.gestureStart) {
|
|
1485
|
+
self.gestureStart(self, panGesture);
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
if (self.animatingDrawer) {
|
|
1489
|
+
[panGesture setEnabled:NO];
|
|
1490
|
+
return;
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
self.startingDrawerSide = [self determineDrawerSideForPanGesture:panGesture];
|
|
1494
|
+
if (self.startingDrawerSide == MMDrawerSideNone) {
|
|
1495
|
+
return;
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
[self setupPanGestureStartingFrameForDrawerSide:self.startingDrawerSide];
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
- (MMDrawerSide)determineDrawerSideForPanGesture:(UIPanGestureRecognizer *)panGesture {
|
|
1502
|
+
MMDrawerSide drawerSide = self.openSide;
|
|
1503
|
+
|
|
1504
|
+
if (drawerSide == MMDrawerSideNone) {
|
|
1505
|
+
CGPoint velocity = [panGesture velocityInView:self.view];
|
|
1506
|
+
drawerSide = (velocity.x > 0) ? MMDrawerSideLeft : MMDrawerSideRight;
|
|
1507
|
+
|
|
1508
|
+
if ((drawerSide == MMDrawerSideLeft && !_leftSideEnabled) ||
|
|
1509
|
+
(drawerSide == MMDrawerSideRight && !_rightSideEnabled)) {
|
|
1510
|
+
drawerSide = (drawerSide == MMDrawerSideLeft) ? MMDrawerSideRight : MMDrawerSideLeft;
|
|
1511
|
+
|
|
1512
|
+
if ((drawerSide == MMDrawerSideLeft && !_leftSideEnabled) ||
|
|
1513
|
+
(drawerSide == MMDrawerSideRight && !_rightSideEnabled)) {
|
|
1514
|
+
return MMDrawerSideNone;
|
|
1515
|
+
}
|
|
1176
1516
|
}
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
return drawerSide;
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
- (void)setupPanGestureStartingFrameForDrawerSide:(MMDrawerSide)drawerSide {
|
|
1523
|
+
MMDrawerOpenMode openMode = [self drawerOpenModeForSide:drawerSide];
|
|
1524
|
+
|
|
1525
|
+
if (openMode == MMDrawerOpenModeAboveContent) {
|
|
1526
|
+
[self setupAboveContentStartingFrameForDrawerSide:drawerSide];
|
|
1527
|
+
} else {
|
|
1528
|
+
self.startingPanRect = self.centerContainerView.frame;
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
- (void)setupAboveContentStartingFrameForDrawerSide:(MMDrawerSide)drawerSide {
|
|
1533
|
+
UIViewController *drawerViewController = [self sideDrawerViewControllerForSide:drawerSide];
|
|
1534
|
+
|
|
1535
|
+
self.startingPanRect = drawerViewController.view.frame;
|
|
1536
|
+
|
|
1537
|
+
// If drawer is closed, set up initial position
|
|
1538
|
+
if (self.openSide == MMDrawerSideNone) {
|
|
1539
|
+
self.startingPanRect = [self calculateClosedDrawerPanStartFrameInOverlay:drawerSide
|
|
1540
|
+
drawerViewController:(UIViewController *)drawerViewController];
|
|
1541
|
+
|
|
1542
|
+
[drawerViewController.view setFrame:self.startingPanRect];
|
|
1543
|
+
|
|
1544
|
+
// Ensure drawer is visible and in front
|
|
1545
|
+
drawerViewController.view.hidden = NO;
|
|
1546
|
+
[self.childControllerContainerView bringSubviewToFront:drawerViewController.view];
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
- (void)handlePanGestureChanged:(UIPanGestureRecognizer *)panGesture {
|
|
1551
|
+
self.view.userInteractionEnabled = NO;
|
|
1552
|
+
|
|
1553
|
+
// Get translation
|
|
1554
|
+
CGPoint translatedPoint = [panGesture translationInView:self.view];
|
|
1555
|
+
|
|
1556
|
+
MMDrawerSide drawerSide = [self determineCurrentDrawerSideWithTranslation:translatedPoint];
|
|
1557
|
+
|
|
1558
|
+
MMDrawerOpenMode openMode = [self drawerOpenModeForSide:drawerSide];
|
|
1559
|
+
|
|
1560
|
+
if (openMode == MMDrawerOpenModeAboveContent) {
|
|
1561
|
+
[self handleOverlayModeGestureChanged:panGesture withTranslation:translatedPoint forDrawerSide:drawerSide];
|
|
1562
|
+
} else {
|
|
1563
|
+
[self handlePushModeGestureChanged:panGesture withTranslation:translatedPoint];
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
self.view.userInteractionEnabled = YES;
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
- (MMDrawerSide)determineCurrentDrawerSideWithTranslation:(CGPoint)translatedPoint {
|
|
1570
|
+
MMDrawerSide drawerSide = self.startingDrawerSide;
|
|
1571
|
+
if (drawerSide == MMDrawerSideNone) {
|
|
1572
|
+
drawerSide = self.openSide;
|
|
1573
|
+
if (drawerSide == MMDrawerSideNone) {
|
|
1574
|
+
drawerSide = (translatedPoint.x > 0) ? MMDrawerSideLeft : MMDrawerSideRight;
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
return drawerSide;
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
- (void)handleOverlayModeGestureChanged:(UIPanGestureRecognizer *)panGesture
|
|
1581
|
+
withTranslation:(CGPoint)translatedPoint
|
|
1582
|
+
forDrawerSide:(MMDrawerSide)drawerSide {
|
|
1583
|
+
UIViewController *drawerViewController = [self sideDrawerViewControllerForSide:drawerSide];
|
|
1584
|
+
if (!drawerViewController) {
|
|
1585
|
+
return;
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
CGFloat maximumDrawerWidth = [self maximumDrawerWidthForSide:drawerSide];
|
|
1589
|
+
|
|
1590
|
+
CGRect newFrame = [self calculateNewFrameForOverlayDrawer:drawerViewController
|
|
1591
|
+
withTranslation:translatedPoint
|
|
1592
|
+
forDrawerSide:drawerSide
|
|
1593
|
+
maximumDrawerWidth:maximumDrawerWidth];
|
|
1594
|
+
|
|
1595
|
+
CGFloat percentVisible = [self calculatePercentVisibleForOverlayDrawer:drawerSide
|
|
1596
|
+
withFrame:newFrame
|
|
1597
|
+
maximumDrawerWidth:maximumDrawerWidth];
|
|
1598
|
+
|
|
1599
|
+
// Handle overlay
|
|
1600
|
+
[self updateOverlayWithPercentVisible:percentVisible];
|
|
1601
|
+
|
|
1602
|
+
MMDrawerSide visibleSide = (percentVisible > 0.01) ? drawerSide : MMDrawerSideNone;
|
|
1603
|
+
|
|
1604
|
+
[self updateAppearanceTransitionsFromSide:self.openSide toSide:visibleSide];
|
|
1605
|
+
|
|
1606
|
+
// Apply new frame
|
|
1607
|
+
drawerViewController.view.frame = newFrame;
|
|
1608
|
+
[self.childControllerContainerView bringSubviewToFront:drawerViewController.view];
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
- (CGRect)calculateNewFrameForOverlayDrawer:(UIViewController *)drawerViewController
|
|
1612
|
+
withTranslation:(CGPoint)translatedPoint
|
|
1613
|
+
forDrawerSide:(MMDrawerSide)drawerSide
|
|
1614
|
+
maximumDrawerWidth:(CGFloat)maximumDrawerWidth {
|
|
1615
|
+
CGRect newFrame = drawerViewController.view.frame;
|
|
1616
|
+
if (self.openSide == drawerSide) {
|
|
1617
|
+
// If drawer is already open, adjust from starting position
|
|
1618
|
+
newFrame.origin.x = self.startingPanRect.origin.x + translatedPoint.x;
|
|
1619
|
+
} else {
|
|
1620
|
+
// If drawer is closed, calculate from off-screen position
|
|
1621
|
+
if (drawerSide == MMDrawerSideLeft) {
|
|
1622
|
+
newFrame.origin.x = -maximumDrawerWidth + translatedPoint.x;
|
|
1180
1623
|
} else {
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
}
|
|
1184
|
-
case UIGestureRecognizerStateChanged: {
|
|
1185
|
-
self.view.userInteractionEnabled = NO;
|
|
1186
|
-
CGRect newFrame = self.startingPanRect;
|
|
1187
|
-
CGPoint translatedPoint = [panGesture translationInView:self.centerContainerView];
|
|
1188
|
-
newFrame.origin.x =
|
|
1189
|
-
[self roundedOriginXForDrawerConstriants:CGRectGetMinX(self.startingPanRect) +
|
|
1190
|
-
translatedPoint.x];
|
|
1191
|
-
newFrame = CGRectIntegral(newFrame);
|
|
1192
|
-
CGFloat xOffset = newFrame.origin.x;
|
|
1193
|
-
|
|
1194
|
-
MMDrawerSide visibleSide = MMDrawerSideNone;
|
|
1195
|
-
CGFloat percentVisible = 0.0;
|
|
1196
|
-
if (xOffset > 0) {
|
|
1197
|
-
visibleSide = MMDrawerSideLeft;
|
|
1198
|
-
percentVisible = xOffset / self.maximumLeftDrawerWidth;
|
|
1199
|
-
} else if (xOffset < 0) {
|
|
1200
|
-
visibleSide = MMDrawerSideRight;
|
|
1201
|
-
percentVisible = ABS(xOffset) / self.maximumRightDrawerWidth;
|
|
1202
|
-
}
|
|
1203
|
-
|
|
1204
|
-
if ((!_leftSideEnabled && visibleSide == MMDrawerSideLeft) ||
|
|
1205
|
-
(!_rightSideEnabled && visibleSide == MMDrawerSideRight)) {
|
|
1206
|
-
return;
|
|
1624
|
+
CGFloat screenWidth = self.view.bounds.size.width;
|
|
1625
|
+
newFrame.origin.x = screenWidth + translatedPoint.x;
|
|
1207
1626
|
}
|
|
1627
|
+
}
|
|
1208
1628
|
|
|
1209
|
-
|
|
1210
|
-
|
|
1629
|
+
// Apply constraints based on drawer side
|
|
1630
|
+
if (drawerSide == MMDrawerSideLeft) {
|
|
1631
|
+
newFrame.origin.x = MIN(0, newFrame.origin.x);
|
|
1632
|
+
newFrame.origin.x = MAX(-maximumDrawerWidth, newFrame.origin.x);
|
|
1633
|
+
} else {
|
|
1634
|
+
CGFloat screenWidth = self.view.bounds.size.width;
|
|
1635
|
+
newFrame.origin.x = MAX(screenWidth - maximumDrawerWidth, newFrame.origin.x);
|
|
1636
|
+
newFrame.origin.x = MIN(screenWidth, newFrame.origin.x);
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
return newFrame;
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
- (CGFloat)calculatePercentVisibleForOverlayDrawer:(MMDrawerSide)drawerSide
|
|
1643
|
+
withFrame:(CGRect)frame
|
|
1644
|
+
maximumDrawerWidth:(CGFloat)maximumDrawerWidth {
|
|
1645
|
+
CGFloat percentVisible;
|
|
1646
|
+
if (drawerSide == MMDrawerSideLeft) {
|
|
1647
|
+
percentVisible = (maximumDrawerWidth + frame.origin.x) / maximumDrawerWidth;
|
|
1648
|
+
} else {
|
|
1649
|
+
CGFloat rightEdge = self.view.bounds.size.width;
|
|
1650
|
+
percentVisible = (rightEdge - frame.origin.x) / maximumDrawerWidth;
|
|
1651
|
+
}
|
|
1652
|
+
return MAX(0, MIN(1.0, percentVisible));
|
|
1653
|
+
}
|
|
1211
1654
|
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1655
|
+
- (void)updateOverlayWithPercentVisible:(CGFloat)percentVisible {
|
|
1656
|
+
[self setupCenterContentOverlay];
|
|
1657
|
+
if (self.centerContentOverlay.superview != self.centerContainerView) {
|
|
1658
|
+
[self.centerContainerView addSubview:self.centerContentOverlay];
|
|
1659
|
+
[self.centerContainerView bringSubviewToFront:self.centerContentOverlay];
|
|
1660
|
+
}
|
|
1661
|
+
self.centerContentOverlay.alpha = percentVisible * 0.5;
|
|
1662
|
+
}
|
|
1218
1663
|
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
[self
|
|
1223
|
-
|
|
1224
|
-
[
|
|
1664
|
+
- (void)updateAppearanceTransitionsFromSide:(MMDrawerSide)fromSide toSide:(MMDrawerSide)toSide {
|
|
1665
|
+
if (fromSide != toSide) {
|
|
1666
|
+
if (fromSide != MMDrawerSideNone) {
|
|
1667
|
+
UIViewController *sideDrawerVC = [self sideDrawerViewControllerForSide:fromSide];
|
|
1668
|
+
[sideDrawerVC beginAppearanceTransition:NO animated:NO];
|
|
1669
|
+
[sideDrawerVC endAppearanceTransition];
|
|
1225
1670
|
}
|
|
1226
1671
|
|
|
1227
|
-
|
|
1672
|
+
if (toSide != MMDrawerSideNone) {
|
|
1673
|
+
[self prepareToPresentDrawer:toSide animated:NO];
|
|
1674
|
+
UIViewController *visibleDrawerVC = [self sideDrawerViewControllerForSide:toSide];
|
|
1675
|
+
[visibleDrawerVC endAppearanceTransition];
|
|
1676
|
+
}
|
|
1228
1677
|
|
|
1229
|
-
[self
|
|
1230
|
-
|
|
1678
|
+
[self setOpenSide:toSide];
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1231
1681
|
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
newFrame.origin.y = floor(newFrame.origin.y);
|
|
1235
|
-
self.centerContainerView.frame = newFrame;
|
|
1682
|
+
- (void)handlePushModeGestureChanged:(UIPanGestureRecognizer *)panGesture withTranslation:(CGPoint)translatedPoint {
|
|
1683
|
+
CGRect newFrame = self.startingPanRect;
|
|
1236
1684
|
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
self.centerContainerView.overlayView.alpha = percentVisible;
|
|
1685
|
+
// Calculate new center container position
|
|
1686
|
+
newFrame.origin.x = self.startingPanRect.origin.x + translatedPoint.x;
|
|
1240
1687
|
|
|
1241
|
-
|
|
1688
|
+
// Apply constraints
|
|
1689
|
+
CGFloat minX = -self.maximumRightDrawerWidth;
|
|
1690
|
+
CGFloat maxX = self.maximumLeftDrawerWidth;
|
|
1691
|
+
newFrame.origin.x = MAX(minX, MIN(maxX, newFrame.origin.x));
|
|
1692
|
+
|
|
1693
|
+
MMDrawerSide visibleSide;
|
|
1694
|
+
CGFloat percentVisible;
|
|
1695
|
+
[self calculateVisibleSideAndPercentage:newFrame.origin.x outSide:&visibleSide outPercentage:&percentVisible];
|
|
1696
|
+
|
|
1697
|
+
if ((!_leftSideEnabled && visibleSide == MMDrawerSideLeft) ||
|
|
1698
|
+
(!_rightSideEnabled && visibleSide == MMDrawerSideRight)) {
|
|
1699
|
+
return;
|
|
1242
1700
|
}
|
|
1243
|
-
|
|
1244
|
-
|
|
1701
|
+
|
|
1702
|
+
[self handlePushModeAppearanceTransitionsForVisibleSide:visibleSide];
|
|
1703
|
+
|
|
1704
|
+
[self updateDrawerVisualStateForDrawerSide:visibleSide percentVisible:percentVisible];
|
|
1705
|
+
[self.centerContainerView setFrame:newFrame];
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
- (void)calculateVisibleSideAndPercentage:(CGFloat)xOffset outSide:(MMDrawerSide *)visibleSide outPercentage:(CGFloat *)percentVisible {
|
|
1709
|
+
*visibleSide = MMDrawerSideNone;
|
|
1710
|
+
*percentVisible = 0.0;
|
|
1711
|
+
|
|
1712
|
+
if (xOffset > 0) {
|
|
1713
|
+
*visibleSide = MMDrawerSideLeft;
|
|
1714
|
+
*percentVisible = xOffset / self.maximumLeftDrawerWidth;
|
|
1715
|
+
} else if (xOffset < 0) {
|
|
1716
|
+
*visibleSide = MMDrawerSideRight;
|
|
1717
|
+
*percentVisible = ABS(xOffset) / self.maximumRightDrawerWidth;
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
- (void)handlePushModeAppearanceTransitionsForVisibleSide:(MMDrawerSide)visibleSide {
|
|
1722
|
+
UIViewController *visibleSideDrawerViewController = [self sideDrawerViewControllerForSide:visibleSide];
|
|
1723
|
+
|
|
1724
|
+
if (self.openSide != visibleSide) {
|
|
1725
|
+
// Handle existing drawer disappearing
|
|
1726
|
+
UIViewController *sideDrawerVC = [self sideDrawerViewControllerForSide:self.openSide];
|
|
1727
|
+
[sideDrawerVC beginAppearanceTransition:NO animated:NO];
|
|
1728
|
+
[sideDrawerVC endAppearanceTransition];
|
|
1729
|
+
|
|
1730
|
+
// Handle new drawer appearing
|
|
1731
|
+
[self prepareToPresentDrawer:visibleSide animated:NO];
|
|
1732
|
+
[visibleSideDrawerViewController endAppearanceTransition];
|
|
1733
|
+
|
|
1734
|
+
[self setOpenSide:visibleSide];
|
|
1735
|
+
} else if (visibleSide == MMDrawerSideNone) {
|
|
1736
|
+
[self setOpenSide:MMDrawerSideNone];
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
- (void)handlePanGestureEnded:(UIPanGestureRecognizer *)panGesture {
|
|
1741
|
+
MMDrawerSide drawerSide = [self determineDrawerSideForGestureEnd:panGesture];
|
|
1742
|
+
|
|
1743
|
+
MMDrawerOpenMode openMode = [self drawerOpenModeForSide:drawerSide];
|
|
1744
|
+
|
|
1745
|
+
if (openMode == MMDrawerOpenModeAboveContent) {
|
|
1746
|
+
[self completeOverlayModeGestureForDrawerSide:drawerSide withPanGesture:panGesture];
|
|
1747
|
+
} else {
|
|
1748
|
+
[self completePushModeGestureWithPanGesture:panGesture];
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1751
|
+
self.startingPanRect = CGRectNull;
|
|
1752
|
+
self.view.userInteractionEnabled = YES;
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
- (MMDrawerSide)determineDrawerSideForGestureEnd:(UIPanGestureRecognizer *)panGesture {
|
|
1756
|
+
MMDrawerSide drawerSide = self.startingDrawerSide;
|
|
1757
|
+
|
|
1758
|
+
if (drawerSide == MMDrawerSideNone) {
|
|
1759
|
+
drawerSide = self.openSide;
|
|
1760
|
+
if (drawerSide == MMDrawerSideNone) {
|
|
1761
|
+
CGPoint velocity = [panGesture velocityInView:self.view];
|
|
1762
|
+
drawerSide = (velocity.x > 0) ? MMDrawerSideLeft : MMDrawerSideRight;
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
return drawerSide;
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
- (void)completeOverlayModeGestureForDrawerSide:(MMDrawerSide)drawerSide withPanGesture:(UIPanGestureRecognizer *)panGesture {
|
|
1770
|
+
// Get drawer view controller
|
|
1771
|
+
UIViewController *drawerVC = [self sideDrawerViewControllerForSide:drawerSide];
|
|
1772
|
+
if (!drawerVC) {
|
|
1773
|
+
self.startingDrawerSide = MMDrawerSideNone;
|
|
1245
1774
|
self.startingPanRect = CGRectNull;
|
|
1246
|
-
CGPoint velocity = [panGesture velocityInView:self.childControllerContainerView];
|
|
1247
|
-
[self finishAnimationForPanGestureWithXVelocity:velocity.x
|
|
1248
|
-
completion:^(BOOL finished) {
|
|
1249
|
-
if (self.gestureCompletion) {
|
|
1250
|
-
self.gestureCompletion(self, panGesture);
|
|
1251
|
-
}
|
|
1252
|
-
}];
|
|
1253
1775
|
self.view.userInteractionEnabled = YES;
|
|
1254
|
-
|
|
1255
|
-
}
|
|
1256
|
-
default:
|
|
1257
|
-
break;
|
|
1776
|
+
return;
|
|
1258
1777
|
}
|
|
1778
|
+
|
|
1779
|
+
BOOL shouldOpen = [self shouldOpenDrawerForGestureEnd:panGesture withDrawerSide:drawerSide andCurrentPosition:drawerVC.view.frame.origin.x];
|
|
1780
|
+
[self animateOverlayDrawerToFinalState:shouldOpen forDrawerSide:drawerSide withDrawerVC:drawerVC andPanGesture:panGesture];
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
- (BOOL)shouldOpenDrawerForGestureEnd:(UIPanGestureRecognizer *)panGesture withDrawerSide:(MMDrawerSide)drawerSide andCurrentPosition:(CGFloat)currentX {
|
|
1784
|
+
CGPoint velocity = [panGesture velocityInView:self.view];
|
|
1785
|
+
CGFloat maximumDrawerWidth = [self maximumDrawerWidthForSide:drawerSide];
|
|
1786
|
+
CGFloat screenWidth = self.view.bounds.size.width;
|
|
1787
|
+
BOOL shouldOpen = NO;
|
|
1788
|
+
|
|
1789
|
+
if (drawerSide == MMDrawerSideLeft) {
|
|
1790
|
+
if (velocity.x > 500) shouldOpen = YES; // Fast right swipe
|
|
1791
|
+
else if (velocity.x < -500) shouldOpen = NO; // Fast left swipe
|
|
1792
|
+
else shouldOpen = (currentX > -maximumDrawerWidth/2.0); // Based on position
|
|
1793
|
+
} else {
|
|
1794
|
+
if (velocity.x < -500) shouldOpen = YES; // Fast left swipe
|
|
1795
|
+
else if (velocity.x > 500) shouldOpen = NO; // Fast right swipe
|
|
1796
|
+
else shouldOpen = (currentX < screenWidth - maximumDrawerWidth/2.0); // Based on position
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
return shouldOpen;
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
- (void)animateOverlayDrawerToFinalState:(BOOL)shouldOpen forDrawerSide:(MMDrawerSide)drawerSide withDrawerVC:(UIViewController *)drawerVC andPanGesture:(UIPanGestureRecognizer *)panGesture {
|
|
1803
|
+
CGFloat maximumDrawerWidth = [self maximumDrawerWidthForSide:drawerSide];
|
|
1804
|
+
CGFloat screenWidth = self.view.bounds.size.width;
|
|
1805
|
+
|
|
1806
|
+
[UIView animateWithDuration:0.25
|
|
1807
|
+
animations:^{
|
|
1808
|
+
CGRect frame = drawerVC.view.frame;
|
|
1809
|
+
|
|
1810
|
+
if (shouldOpen) {
|
|
1811
|
+
if (drawerSide == MMDrawerSideLeft) {
|
|
1812
|
+
frame.origin.x = 0;
|
|
1813
|
+
} else {
|
|
1814
|
+
frame.origin.x = screenWidth - maximumDrawerWidth;
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
self.centerContentOverlay.alpha = 0.5;
|
|
1818
|
+
} else {
|
|
1819
|
+
if (drawerSide == MMDrawerSideLeft) {
|
|
1820
|
+
frame.origin.x = -maximumDrawerWidth;
|
|
1821
|
+
} else {
|
|
1822
|
+
frame.origin.x = screenWidth;
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
self.centerContentOverlay.alpha = 0.0;
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
drawerVC.view.frame = frame;
|
|
1829
|
+
} completion:^(BOOL finished) {
|
|
1830
|
+
if (shouldOpen) {
|
|
1831
|
+
[self setOpenSide:drawerSide];
|
|
1832
|
+
} else {
|
|
1833
|
+
[self setOpenSide:MMDrawerSideNone];
|
|
1834
|
+
[self.centerContentOverlay removeFromSuperview];
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1837
|
+
self.startingDrawerSide = MMDrawerSideNone;
|
|
1838
|
+
|
|
1839
|
+
if (self.gestureCompletion) {
|
|
1840
|
+
self.gestureCompletion(self, panGesture);
|
|
1841
|
+
}
|
|
1842
|
+
}];
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
- (void)completePushModeGestureWithPanGesture:(UIPanGestureRecognizer *)panGesture {
|
|
1846
|
+
CGPoint velocity = [panGesture velocityInView:self.childControllerContainerView];
|
|
1847
|
+
[self finishAnimationForPanGestureWithXVelocity:velocity.x
|
|
1848
|
+
completion:^(BOOL finished) {
|
|
1849
|
+
if (self.gestureCompletion) {
|
|
1850
|
+
self.gestureCompletion(self, panGesture);
|
|
1851
|
+
}
|
|
1852
|
+
}];
|
|
1259
1853
|
}
|
|
1260
1854
|
|
|
1261
1855
|
- (void)updatePanHandlersState {
|
|
@@ -1270,6 +1864,29 @@ static NSString *MMDrawerOpenSideKey = @"MMDrawerOpenSide";
|
|
|
1270
1864
|
}
|
|
1271
1865
|
}
|
|
1272
1866
|
|
|
1867
|
+
- (void)setupCenterContentOverlay {
|
|
1868
|
+
if (!self.centerContentOverlay) {
|
|
1869
|
+
// Create overlay view if it doesn't exist
|
|
1870
|
+
self.centerContentOverlay = [[UIView alloc] initWithFrame:self.centerContainerView.bounds];
|
|
1871
|
+
self.centerContentOverlay.backgroundColor = [UIColor blackColor];
|
|
1872
|
+
self.centerContentOverlay.alpha = 0.0; // Start fully transparent
|
|
1873
|
+
self.centerContentOverlay.userInteractionEnabled = YES;
|
|
1874
|
+
|
|
1875
|
+
// Add tap gesture to close drawer when tapping overlay
|
|
1876
|
+
UITapGestureRecognizer *tapGesture =
|
|
1877
|
+
[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(overlayTapped:)];
|
|
1878
|
+
[self.centerContentOverlay addGestureRecognizer:tapGesture];
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
// Update frame to match current center container bounds
|
|
1882
|
+
self.centerContentOverlay.frame = self.centerContainerView.bounds;
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
- (void)overlayTapped:(UITapGestureRecognizer *)tapGesture {
|
|
1886
|
+
// Close drawer when overlay is tapped
|
|
1887
|
+
[self closeDrawerAnimated:YES completion:nil];
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1273
1890
|
#pragma mark - iOS 7 Status Bar Helpers
|
|
1274
1891
|
- (UIViewController *)childViewControllerForStatusBarStyle {
|
|
1275
1892
|
return [self childViewControllerForSide:self.openSide];
|
|
@@ -1367,23 +1984,46 @@ static NSString *MMDrawerOpenSideKey = @"MMDrawerOpenSide";
|
|
|
1367
1984
|
}
|
|
1368
1985
|
}
|
|
1369
1986
|
|
|
1987
|
+
- (void)side:(MMDrawerSide)drawerSide openMode:(MMDrawerOpenMode)openMode {
|
|
1988
|
+
if (drawerSide == MMDrawerSideLeft) {
|
|
1989
|
+
self.leftDrawerOpenMode = openMode;
|
|
1990
|
+
} else if (drawerSide == MMDrawerSideRight) {
|
|
1991
|
+
self.rightDrawerOpenMode = openMode;
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
|
|
1370
1995
|
- (void)applyOvershootScaleTransformForDrawerSide:(MMDrawerSide)drawerSide
|
|
1371
1996
|
percentVisible:(CGFloat)percentVisible {
|
|
1372
|
-
|
|
1373
1997
|
if (percentVisible >= 1.f) {
|
|
1374
1998
|
CATransform3D transform = CATransform3DIdentity;
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
transform,
|
|
1999
|
+
|
|
2000
|
+
MMDrawerOpenMode openMode = [self drawerOpenModeForSide:drawerSide];
|
|
2001
|
+
UIViewController *sideDrawerViewController = [self sideDrawerViewControllerForSide:drawerSide];
|
|
2002
|
+
|
|
2003
|
+
if (openMode == MMDrawerOpenModePushContent) {
|
|
2004
|
+
CGFloat stretchFactor = MIN(percentVisible, 1.1);
|
|
2005
|
+
|
|
2006
|
+
if (drawerSide == MMDrawerSideLeft) {
|
|
2007
|
+
transform = CATransform3DMakeScale(stretchFactor, 1.f, 1.f);
|
|
2008
|
+
transform = CATransform3DTranslate(transform, self.maximumLeftDrawerWidth * (stretchFactor - 1.f) / 2, 0.f, 0.f);
|
|
2009
|
+
} else if (drawerSide == MMDrawerSideRight) {
|
|
2010
|
+
transform = CATransform3DMakeScale(stretchFactor, 1.f, 1.f);
|
|
2011
|
+
transform = CATransform3DTranslate(transform, -self.maximumRightDrawerWidth * (stretchFactor - 1.f) / 2, 0.f, 0.f);
|
|
2012
|
+
}
|
|
2013
|
+
|
|
2014
|
+
sideDrawerViewController.view.layer.transform = transform;
|
|
2015
|
+
}
|
|
2016
|
+
else if (openMode == MMDrawerOpenModeAboveContent) {
|
|
2017
|
+
if (drawerSide == MMDrawerSideLeft) {
|
|
2018
|
+
transform = CATransform3DMakeScale(percentVisible, 1.f, 1.f);
|
|
2019
|
+
transform = CATransform3DTranslate(transform, self.maximumLeftDrawerWidth * (percentVisible - 1.f) / 2, 0.f, 0.f);
|
|
2020
|
+
} else if (drawerSide == MMDrawerSideRight) {
|
|
2021
|
+
transform = CATransform3DMakeScale(percentVisible, 1.f, 1.f);
|
|
2022
|
+
transform = CATransform3DTranslate(transform, -self.maximumRightDrawerWidth * (percentVisible - 1.f) / 2, 0.f, 0.f);
|
|
2023
|
+
}
|
|
2024
|
+
|
|
2025
|
+
sideDrawerViewController.view.layer.transform = transform;
|
|
1385
2026
|
}
|
|
1386
|
-
sideDrawerViewController.view.layer.transform = transform;
|
|
1387
2027
|
}
|
|
1388
2028
|
}
|
|
1389
2029
|
|
|
@@ -1679,4 +2319,18 @@ static inline CGFloat originXForDrawerOriginAndTargetOriginOffset(CGFloat origin
|
|
|
1679
2319
|
return (CGRectContainsPoint(rightBezelRect, point) &&
|
|
1680
2320
|
[self isPointContainedWithinCenterViewContentRect:point]);
|
|
1681
2321
|
}
|
|
2322
|
+
|
|
2323
|
+
- (MMDrawerOpenMode)drawerOpenModeForSide:(MMDrawerSide)drawerSide {
|
|
2324
|
+
return (drawerSide == MMDrawerSideLeft) ? self.leftDrawerOpenMode : self.rightDrawerOpenMode;
|
|
2325
|
+
}
|
|
2326
|
+
|
|
2327
|
+
- (CGFloat)maximumDrawerWidthForSide:(MMDrawerSide)drawerSide {
|
|
2328
|
+
return (drawerSide == MMDrawerSideLeft) ? self.maximumLeftDrawerWidth : self.maximumRightDrawerWidth;
|
|
2329
|
+
}
|
|
2330
|
+
|
|
2331
|
+
// Helper method to calculate animation duration based on distance and velocity
|
|
2332
|
+
- (NSTimeInterval)animationDurationForDistance:(CGFloat)distance velocity:(CGFloat)velocity {
|
|
2333
|
+
return MAX(distance / ABS(velocity), MMDrawerMinimumAnimationDuration);
|
|
2334
|
+
}
|
|
2335
|
+
|
|
1682
2336
|
@end
|