ECSlidingViewController:显示左侧菜单时模糊视图控制器

ECSlidingViewController: blur view controller when displaying left menu

我在我的应用程序中使用 ECSlidingViewController 来显示左侧菜单。

有什么方法可以让主视图控制器 'darker' 移动并显示左侧菜单时?

谢谢。

我解决了这个问题。 只需从示例中获取一些 类 并更改它:

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