Search code examples
iosecslidingviewcontroller

ECSlidingViewController: blur view controller when displaying left menu


I'm using ECSlidingViewController in my application to display left menu.

Is there are any way to make main view controller 'darker' when it's moved and left menu is displayed?

Thanks.


Solution

  • I solved this issue. Just get some classes from example and changed it:

    CPSlidingAnimationController:

    #import "CPSlidingAnimationController.h"
    #import "ECSlidingAnimationController.h"
    #import "ECSlidingConstants.h"
    
    @interface CPSlidingAnimationController ()
    @property (nonatomic, copy) void (^coordinatorAnimations)(id<UIViewControllerTransitionCoordinatorContext>context);
    @property (nonatomic, copy) void (^coordinatorCompletion)(id<UIViewControllerTransitionCoordinatorContext>context);
    @end
    
    @implementation CPSlidingAnimationController
    @synthesize transition, animationFinishCallback;
    
    #pragma mark - UIViewControllerAnimatedTransitioning
    
    - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
         return 0.25;
    }
    
    - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
        UIViewController *topViewController = [transitionContext viewControllerForKey:ECTransitionContextTopViewControllerKey];
        UIViewController *toViewController  = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
        UIView *containerView = [transitionContext containerView];
        CGRect topViewInitialFrame = [transitionContext initialFrameForViewController:topViewController];
        CGRect topViewFinalFrame   = [transitionContext finalFrameForViewController:topViewController];
    
        topViewController.view.frame = topViewInitialFrame;
    
        if (topViewController != toViewController) {
            CGRect toViewFinalFrame = [transitionContext finalFrameForViewController:toViewController];
            toViewController.view.frame = toViewFinalFrame;
            [containerView insertSubview:toViewController.view belowSubview:topViewController.view];
        }
    
        NSTimeInterval duration = [self transitionDuration:transitionContext];
        [UIView animateWithDuration:duration
                         animations:^{
                             [UIView setAnimationCurve:UIViewAnimationCurveLinear];
                             if (self.coordinatorAnimations) self.coordinatorAnimations((id<UIViewControllerTransitionCoordinatorContext>)transitionContext);
                             topViewController.view.frame = topViewFinalFrame;
                         } completion:^(BOOL finished) {
                             if ([transitionContext transitionWasCancelled]) {
                                 topViewController.view.frame = [transitionContext initialFrameForViewController:topViewController];
                             }
    
                             if (self.coordinatorCompletion) self.coordinatorCompletion((id<UIViewControllerTransitionCoordinatorContext>)transitionContext);
                             [transitionContext completeTransition:finished];
    
                             if (transition) {
                                 if ([(NSObject*) transition respondsToSelector:animationFinishCallback]) {
                                     [transition performSelector:animationFinishCallback];
                                 }
                             }
                         }];
    }
    
    @end
    

    MEDynamicTransition.h:

    #import "MEDynamicTransition.h"
    #import "CPSlidingAnimationController.h"
    
    @interface MEDynamicTransition () {
        UIVisualEffectView * effectView;
    }
    
    @property (nonatomic, strong) CPSlidingAnimationController * defaultAnimationController;
    @property (nonatomic, strong) NSMutableArray *leftEdgeQueue;
    @property (nonatomic, assign) id<UIViewControllerContextTransitioning> transitionContext;
    @property (nonatomic, strong) UIDynamicAnimator *animator;
    @property (nonatomic, strong) UICollisionBehavior *collisionBehavior;
    @property (nonatomic, strong) UIGravityBehavior *gravityBehavior;
    @property (nonatomic, strong) UIPushBehavior *pushBehavior;
    @property (nonatomic, strong) UIDynamicItemBehavior *topViewBehavior;
    @property (nonatomic, strong) UIDynamicBehavior *compositeBehavior;
    @property (nonatomic, assign) BOOL positiveLeftToRight;
    @property (nonatomic, assign) BOOL isPanningRight;
    @property (nonatomic, assign) BOOL isInteractive;
    @property (nonatomic, assign) CGFloat fullWidth;
    @property (nonatomic, assign) CGRect initialTopViewFrame;
    @end
    
    @implementation MEDynamicTransition
    
    #pragma mark - ECSlidingViewControllerDelegate
    
    - (id<UIViewControllerAnimatedTransitioning>)slidingViewController:(ECSlidingViewController *)slidingViewController
                                       animationControllerForOperation:(ECSlidingViewControllerOperation)operation
                                                     topViewController:(UIViewController *)topViewController {
    
        return self.defaultAnimationController;
    }
    
    - (id<UIViewControllerInteractiveTransitioning>)slidingViewController:(ECSlidingViewController *)slidingViewController
                              interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>)animationController {
        self.slidingViewController = slidingViewController;
        return self;
    }
    
    #pragma mark - Properties
    
    - (CPSlidingAnimationController *)defaultAnimationController {
        if (_defaultAnimationController) return _defaultAnimationController;
    
        _defaultAnimationController = [[CPSlidingAnimationController alloc] init];
        _defaultAnimationController.transition = self;
        _defaultAnimationController.animationFinishCallback = @selector(blurControl);
    
        return _defaultAnimationController;
    }
    
    - (NSMutableArray *)leftEdgeQueue {
        if (_leftEdgeQueue) return _leftEdgeQueue;
    
        _leftEdgeQueue = [NSMutableArray arrayWithCapacity:5];
    
        return _leftEdgeQueue;
    }
    
    - (UIDynamicAnimator *)animator {
        if (_animator) return _animator;
    
        _animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.slidingViewController.view];
        _animator.delegate = self;
        [_animator updateItemUsingCurrentState:self.slidingViewController.topViewController.view];
    
        return _animator;
    }
    
    - (UICollisionBehavior *)collisionBehavior {
        if (_collisionBehavior) return _collisionBehavior;
    
        _collisionBehavior = [[UICollisionBehavior alloc] initWithItems:@[self.slidingViewController.topViewController.view]];
    
        CGFloat containerHeight = self.slidingViewController.view.bounds.size.height;
        CGFloat containerWidth  = self.slidingViewController.view.bounds.size.width;
        CGFloat revealAmount    = self.slidingViewController.anchorRightRevealAmount;
    
        [_collisionBehavior addBoundaryWithIdentifier:@"LeftEdge" fromPoint:CGPointMake(-1, 0) toPoint:CGPointMake(-1, containerHeight)];
        [_collisionBehavior addBoundaryWithIdentifier:@"RightEdge" fromPoint:CGPointMake(revealAmount + containerWidth + 1, 0) toPoint:CGPointMake(revealAmount + containerWidth + 1, containerHeight)];
    
        return _collisionBehavior;
    }
    
    - (UIGravityBehavior *)gravityBehavior {
        if (_gravityBehavior) return _gravityBehavior;
    
        _gravityBehavior = [[UIGravityBehavior alloc] initWithItems:@[self.slidingViewController.topViewController.view]];
    
        return _gravityBehavior;
    }
    
    - (UIPushBehavior *)pushBehavior {
        if (_pushBehavior) return _pushBehavior;
    
        _pushBehavior = [[UIPushBehavior alloc] initWithItems:@[self.slidingViewController.topViewController.view] mode:UIPushBehaviorModeInstantaneous];
    
        return _pushBehavior;
    }
    
    - (UIDynamicItemBehavior *)topViewBehavior {
        if (_topViewBehavior) return _topViewBehavior;
    
        UIView *topView = self.slidingViewController.topViewController.view;
        _topViewBehavior = [[UIDynamicItemBehavior alloc] initWithItems:@[topView]];
        // the density ranges from 1 to 5 for iPad to iPhone
        _topViewBehavior.density = 908800 / (topView.bounds.size.width * topView.bounds.size.height);
        _topViewBehavior.elasticity = 0;
        _topViewBehavior.resistance = 1;
    
        return _topViewBehavior;
    }
    
    - (UIDynamicBehavior *)compositeBehavior {
        if (_compositeBehavior) return _compositeBehavior;
    
        _compositeBehavior = [[UIDynamicBehavior alloc] init];
        [_compositeBehavior addChildBehavior:self.collisionBehavior];
        [_compositeBehavior addChildBehavior:self.gravityBehavior];
        [_compositeBehavior addChildBehavior:self.pushBehavior];
        [_compositeBehavior addChildBehavior:self.topViewBehavior];
        __weak typeof(self)weakSelf = self;
        _compositeBehavior.action = ^{
            // stop the dynamic animation when the value of the left edge is the same 5 times in a row.
            NSNumber *leftEdge = [NSNumber numberWithFloat:weakSelf.slidingViewController.topViewController.view.frame.origin.x];
            [weakSelf.leftEdgeQueue insertObject:leftEdge atIndex:0];
            if (weakSelf.leftEdgeQueue.count == 6) [weakSelf.leftEdgeQueue removeLastObject];
    
            if (weakSelf.leftEdgeQueue.count == 5 &&
                ((NSArray *)[weakSelf.leftEdgeQueue valueForKeyPath:@"@distinctUnionOfObjects.self"]).count == 1) {
                [weakSelf.animator removeAllBehaviors];
            }
        };
    
        return _compositeBehavior;
    }
    
    #pragma mark - UIViewControllerInteractiveTransitioning
    
    - (void)startInteractiveTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
        self.transitionContext = transitionContext;
    
        UIViewController *topViewController = [transitionContext viewControllerForKey:ECTransitionContextTopViewControllerKey];
        topViewController.view.userInteractionEnabled = NO;
    
        if (_isInteractive) {
            UIViewController *underViewController = [transitionContext viewControllerForKey:ECTransitionContextUnderLeftControllerKey];
            CGRect underViewInitialFrame = [transitionContext initialFrameForViewController:underViewController];
            CGRect underViewFinalFrame   = [transitionContext finalFrameForViewController:underViewController];
            UIView *containerView = [transitionContext containerView];
            CGFloat finalLeftEdge = CGRectGetMinX([transitionContext finalFrameForViewController:topViewController]);
            CGFloat initialLeftEdge = CGRectGetMinX([transitionContext initialFrameForViewController:topViewController]);
            CGFloat fullWidth = fabs(finalLeftEdge - initialLeftEdge);
    
            CGRect underViewFrame;
            if (CGRectIsEmpty(underViewInitialFrame)) {
                underViewFrame = underViewFinalFrame;
            } else {
                underViewFrame = underViewInitialFrame;
            }
    
            underViewController.view.frame = underViewFrame;
    
            [containerView insertSubview:underViewController.view belowSubview:topViewController.view];
    
            self.positiveLeftToRight = initialLeftEdge < finalLeftEdge;
            self.fullWidth           = fullWidth;
        } else {
            [self.defaultAnimationController animateTransition:transitionContext];
        }
    }
    
    #pragma mark - UIPanGestureRecognizer action
    
    - (void)handlePanGesture:(UIPanGestureRecognizer *)recognizer {
        if ([self.animator isRunning]) return;
    
        UIView *topView       = self.slidingViewController.topViewController.view;
        CGFloat translationX  = [recognizer translationInView:self.slidingViewController.view].x;
        CGFloat velocityX     = [recognizer velocityInView:self.slidingViewController.view].x;
    
        // Blur effect
        [self blurControl];
    
        switch (recognizer.state) {
            case UIGestureRecognizerStateBegan: {
                BOOL isMovingRight = velocityX > 0;
    
                CALayer *presentationLayer = (CALayer *)topView.layer.presentationLayer;
                self.initialTopViewFrame = presentationLayer.frame;
    
                _isInteractive = YES;
    
                if (self.slidingViewController.currentTopViewPosition == ECSlidingViewControllerTopViewPositionCentered && isMovingRight && self.slidingViewController.underLeftViewController) {
                    [self.slidingViewController anchorTopViewToRightAnimated:YES];
                } else if (self.slidingViewController.currentTopViewPosition == ECSlidingViewControllerTopViewPositionCentered && !isMovingRight && self.slidingViewController.underRightViewController) {
                    [self.slidingViewController anchorTopViewToLeftAnimated:YES];
                } else if (self.slidingViewController.currentTopViewPosition == ECSlidingViewControllerTopViewPositionAnchoredLeft) {
                    [self.slidingViewController resetTopViewAnimated:YES];
                } else if (self.slidingViewController.currentTopViewPosition == ECSlidingViewControllerTopViewPositionAnchoredRight) {
                    [self.slidingViewController resetTopViewAnimated:YES];
                } else {
                    _isInteractive = NO;
                }
    
                break;
            }
            case UIGestureRecognizerStateChanged: {
                if (!_isInteractive) return;
    
                CGRect topViewInitialFrame = self.initialTopViewFrame;
                CGFloat newLeftEdge = topViewInitialFrame.origin.x + translationX;
    
                if (newLeftEdge < 0) {
                    newLeftEdge = 0;
                } else if (newLeftEdge > self.slidingViewController.anchorRightRevealAmount) {
                    newLeftEdge = self.slidingViewController.anchorRightRevealAmount;
                }
    
                topViewInitialFrame.origin.x = newLeftEdge;
                topView.frame = topViewInitialFrame;
    
                if (!self.positiveLeftToRight) translationX = translationX * -1.0;
                CGFloat percentComplete = (translationX / self.fullWidth);
                if (percentComplete < 0) percentComplete = 0;
                if (percentComplete > 100) percentComplete = 100;
                [self.transitionContext updateInteractiveTransition:percentComplete];
                break;
            }
            case UIGestureRecognizerStateEnded:
            {
                [self blurControl];
            };
            case UIGestureRecognizerStateCancelled: {
                if (!_isInteractive) return;
    
                _isInteractive = NO;
    
                self.isPanningRight = velocityX > 0;
    
                if (self.isPanningRight) {
                    [self blurControl];
                } else {
                    [self disableBlur];
                }
    
                self.gravityBehavior.gravityDirection = self.isPanningRight ? CGVectorMake(2, 0) : CGVectorMake(-2, 0);
    
                self.pushBehavior.angle = 0; // velocity may be negative
                self.pushBehavior.magnitude = velocityX;
                self.pushBehavior.active = YES;
    
                [self.animator addBehavior:self.compositeBehavior];
    
                break;
            }
            default:
                break;
        }
    }
    
    #pragma mark - UIDynamicAnimatorDelegate
    
    - (void)dynamicAnimatorDidPause:(UIDynamicAnimator*)animator {
        [self.animator removeAllBehaviors];
    
        _collisionBehavior = nil;
        _topViewBehavior = nil;
        _pushBehavior = nil;
        _gravityBehavior = nil;
        _compositeBehavior = nil;
        _animator = nil;
    
        self.slidingViewController.topViewController.view.userInteractionEnabled = YES;
        UIViewController *topViewController = [self.transitionContext viewControllerForKey:ECTransitionContextTopViewControllerKey];
        if ((self.isPanningRight && self.positiveLeftToRight) || (!self.isPanningRight && !self.positiveLeftToRight)) {
            topViewController.view.frame = [self.transitionContext finalFrameForViewController:topViewController];
            [self.transitionContext finishInteractiveTransition];
        } else if ((self.isPanningRight && !self.positiveLeftToRight) || (!self.isPanningRight && self.positiveLeftToRight)) {
            topViewController.view.frame = [self.transitionContext initialFrameForViewController:topViewController];
            [self.transitionContext cancelInteractiveTransition];
        }
    
        [self.transitionContext completeTransition:YES];
    }
    
    #pragma mark - Blur
    
    - (void) setBlurOffset:(CGFloat) offset {
        UIView *topView = self.slidingViewController.topViewController.view;
    
        if (!effectView) {
            UIBlurEffect * blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark];
            effectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];
            effectView.frame = topView.bounds;
            effectView.alpha = 0.1f;
            [topView addSubview:effectView];
    
            [effectView setTranslatesAutoresizingMaskIntoConstraints:false];
            [topView addConstraint:[NSLayoutConstraint constraintWithItem:effectView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:topView attribute:NSLayoutAttributeTop multiplier:1 constant:0]];
            [topView addConstraint:[NSLayoutConstraint constraintWithItem:effectView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:topView attribute:NSLayoutAttributeBottom multiplier:1 constant:0]];
            [topView addConstraint:[NSLayoutConstraint constraintWithItem:effectView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:topView attribute:NSLayoutAttributeLeading multiplier:1 constant:0]];
            [topView addConstraint:[NSLayoutConstraint constraintWithItem:effectView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:topView attribute:NSLayoutAttributeTrailing multiplier:1 constant:0]];
        }
    
        CGFloat c = offset / 100;
        if (c < 0.1f) c = 0.1f; else if (c > 0.5f) c = 0.5f;
        effectView.alpha = c;
    }
    
    - (void) disableBlur{
        if (effectView != NULL) {
            [effectView removeFromSuperview];
            effectView = NULL;
        }
    }
    
    - (void) blurControl{
        UIView *topView = self.slidingViewController.topViewController.view;
        NSLog(@"X: %f", topView.frame.origin.x);
    
        if (topView.frame.origin.x >= 15) {
            [self setBlurOffset:topView.frame.origin.x];
        } else {
            [self disableBlur];
        }
    
    }
    @end