/APTransitionDirector

APTransitionDirector

Primary LanguageObjective-CMIT LicenseMIT

APTransitionDirector

APTransitionDirector

Example Usage

You could use director in one of this ways

  • Basical Usage (for interactive and staic trasitions)
  • category (APTransitions) & APTransitionProtocol (for static transitions)
  • category (APTransitions) & blocks (for static transitions) - not implemented yet
  • category (APRuleInteractiveTransitions) & APTransitionRule (for interactive transitions)
  • category (APInteractiveTransitions) (for interactive transitions, the same as pervious but faster to implement) - not inplemented yet and more...

Basical Usage

The main class is APTransitionDirector - its responds of all trasition protocols that iOS have:

<UIViewControllerAnimatedTransitioning,UINavigationControllerDelegate,UIViewControllerInteractiveTransitioning,UITabBarControllerDelegate,UIViewControllerTransitioningDelegate>

its super fast to create u own transition, even with basical usage(see the category usage below):

APTransitionDirector * director=[[APTransitionDirector alloc]init];
director.animBlock=^(APTransitionDirector * director ,void(^comlitBlock)() ){

};
self.navigationController.delegate=director;
[self.navigationController pushViewController:[[BUViewController alloc] init] animated:YES]
self.navigationController.delegate=nil;

Now you have the animation block ,where u could use any animations that u want,to get all needed views use the APFastAcces category of APTransitionDirector. Lets Add Some Cool Animations:

//BURootViewController.m
APTransitionDirector * director=[[APTransitionDirector alloc]init];
director.animDuration=0.5; //animation duration for director
director.animBlock=^(APTransitionDirector * director ,void(^comlitBlock)() ){
    //getting all needed views
    UIView* toView = [director toView];
    UIView* fromView= [director fromView];
    UIView *  containerView=  [director containerView];
   //presetup
    [containerView insertSubview:toView aboveSubview:fromView];
    [toView setFrame:CGRectMake(containerView.frame.size.width, toView.frame.origin.y, toView.frame.size.width, toView.frame.size.height)];
    fromView.transform=CGAffineTransformMakeScale(1.0, 1.0);
    //animation block
    [UIView animateWithDuration:director.animDuration delay:0 usingSpringWithDamping:0.8 initialSpringVelocity:0.1 options:UIViewAnimationOptionCurveEaseIn
    animations:^{
        [toView setCenter:CGPointMake(containerView.frame.size.width/2, toView.center.y)];
        fromView.transform=CGAffineTransformMakeScale(0.9, 0.9);
        } completion:^(BOOL finished) {
            fromView.transform=CGAffineTransformMakeScale(1.0, 1.0);
  //dont forget to call the complition block
            comlitBlock();
    }];

};
self.navigationController.delegate=director;
[self.navigationController pushViewController:viewController animated:YES]
self.navigationController.delegate=nil;

Pls note: When u use non interactive animations dont forget to call the complitBlock Also u could use @property (nonatomic)float animDuration; to make all things clear.

Ok,Great! But we could make it better by adding some interactive transitions. Let add EdgePanGesture to our navigationController:

//BURootViewController.m
UIScreenEdgePanGestureRecognizer * panGesture = [[UIScreenEdgePanGestureRecognizer alloc]initWithTarget:self action:@selector(screenPan:)];
[panGesture setDelegate:self];
panGesture.edges=UIRectEdgeLeft;
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
[self.navigationController.view addGestureRecognizer:panGesture];

and the screenPan Method:

//BURootViewController.m
- (void)screenPan:(UIGestureRecognizer*)pan {
    CGPoint location = [pan locationInView:self.view];
    static CGPoint firstTouch;
    static float fullDistance=0;
    static APTransitionDirector * animDirector=nil;

    switch (pan.state) {
        case UIGestureRecognizerStateBegan:
        {

            fullDistance=self.navigationController.view.frame.size.width;
            firstTouch=location;

            animDirector=[[APTransitionDirector alloc]init];
            animDirector.interactive=YES;
            animDirector.animDuration=0.33;
            [self.navigationController setDelegate:animDirector];

            animDirector.animBlock=^(APTransitionDirector * director,void(^comlitBlock)() ){

                UIView* toView = [director toView];
                UIView* fromView= [director fromView];
                UIView *  containerView=  [director containerView];

                [containerView insertSubview:toView belowSubview:fromView];
                toView.transform=CGAffineTransformMakeScale(1.0, 1.0);
                [toView setFrame:containerView.bounds];


                toView.transform=CGAffineTransformMakeScale(0.9, 0.9);

                [ UIView animateWithDuration:director.animDuration
                    animations:^{

                    [fromView setFrame:CGRectMake(fromView.frame.size.width, fromView.frame.origin.y, fromView.frame.size.width, fromView.frame.size.height)];
                    toView.transform=CGAffineTransformMakeScale(1.0, 1.0);

                }completion:^(BOOL finished) {

                }];
            };
            [self.navigationController popViewControllerAnimated:YES];
            break;
        case  UIGestureRecognizerStateChanged:
        {
            //update percent for every step
            [animDirector setPercent:location.x/fullDistance];
            break;
        }
        case UIGestureRecognizerStateCancelled:
        case UIGestureRecognizerStateEnded: {

            BOOL didComplete=NO;
            if (pan.state==UIGestureRecognizerStateEnded){
            didComplete = (location.x/fullDistance)>0.5?YES:NO;
            }

            //and end interactive transition at state ended or canceled
            [animDirector endInteractiveTranscation:didComplete complition:^{

            }];
            animDirector = nil;
            self.navigationController.delegate = nil;

            break;
        }
        default: {

            break;
        }

    }
  }

}

So the main things that u should do:

  • set director as interactive animDirector.interactive=YES;
  • update director percent [animDirector setPercent:location.x/fullDistance];
  • run director interactive endingMethod: [animDirector endInteractiveTranscation:didComplete complition:^{}];

That looks nice but wee need some more, lets use the @property (copy)UpdateBlock interactiveUpdateBlock, so we could create unique interactive transaction :

  • Let add new method that will create maskLayer to "fromView"
-(void)addMaskToView:(UIView*)view withPosition:(CGPoint)maskPosition{
    CAShapeLayer * maskLayer;
    if (!view.layer.mask) {
        maskLayer=[CAShapeLayer layer];
        view.layer.mask=maskLayer;
        maskLayer.fillRule=kCAFillRuleEvenOdd;
    }else{
        maskLayer=(CAShapeLayer*)view.layer.mask;
    }
        CGMutablePathRef path=CGPathCreateMutable();
        CGPathAddRect(path, nil, view.bounds);
        CGPathAddEllipseInRect(path, nil, CGRectMake(-28, maskPosition.y, 44, 44));
        maskLayer.path=path;
        CGPathRelease(path);
}
  • make some updates in our UIGestureRecognizerStateEnded and UIGestureRecognizerStateChanged:
//.....
case  UIGestureRecognizerStateChanged:
{

    animDirector.interactiveUpdateBlock=^(APTransitionDirector*director){
    UIView* fromView= [director fromView];
    [self addMaskToView:fromView withPosition:location];
    };
    //update percent for every step
    [animDirector setPercent:location.x/fullDistance];
    break;
}

case UIGestureRecognizerStateCancelled:
case UIGestureRecognizerStateEnded: {

    BOOL didComplete=NO;
    if (pan.state==UIGestureRecognizerStateEnded){
        didComplete = (location.x/fullDistance)>0.5?YES:NO;
    }   

        //and end interactive transition at state ended or canceled
    [animDirector endInteractiveTranscation:didComplete complition:^(APTransitionDirector*director){
    [director fromView].layer.mask=nil;

    }];
    animDirector = nil;
    self.navigationController.delegate = nil;
    break;
}
....

Ok, so now u have nice interactive and static transition.This code is in examples as well (BasicUsage-NavigationController) u also could check some others examples there,like Basic Usage with TabbarController and ViewController. Basic Usage NavigationController

As u maybe noticed there are a little bit of extra code.To avoid this u could use APTransitionProtocol and animDirector.delegate=someObject; Its pretty easy to use, u could see this in BasicUsage(APTransitionProtocol)-NavigationController. As well u didnt need always to call animDirector=nil or self.navigationController=nil ,its up to you.(see the asicUsage(APTransitionProtocol)-NavigationController example)

Category (APTransitions) & APTransitionProtocol