自定义转场

自定义转场_第1张图片
效果图 Gif.gif

设备支持的翻转有哪些 , 程序在设备左转再左转之后打印出的日志是程序方向不变 , 即:设备倒向 , 程序不是倒向反而还是左向;
transitioningDelegate:告诉系统我的转场动画要自定义而不是默认;
UIViewControllerAnimatedTransitioning:两个代理方法:
- (NSTimeInterval)transitionDuration:(id)transitionContext- (void)animateTransition:(id)transitionContext

AppDelegate

#import "AppDelegate.h"
#import "TestViewController.h"
#import "CustomNavigationControllerDelegate.h"

@interface AppDelegate ()
{
    CustomNavigationControllerDelegate *_customNavigationDelegate;
}
@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.backgroundColor = [UIColor whiteColor];
    TestViewController *testVC = [[TestViewController alloc] init];
    UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:testVC];
    _customNavigationDelegate = [[CustomNavigationControllerDelegate alloc] init];
    nav.delegate = _customNavigationDelegate;
    self.window.rootViewController = nav;
    [self.window makeKeyAndVisible];
    return YES;
    
}

@end

TestViewController.m

#import "TestViewController.h"
#import "TestSecondViewController.h"
#import "PresentAnimation.h"
#import "DismissAnimation.h"


@interface TestViewController () 
{
    UIButton *_button;
    UITextField *_textField;
    PresentAnimation *_presentAnimation;
    DismissAnimation *_dismissAnimation;
    
    UIButton *_pushButton;
}

@end

@implementation TestViewController


/**
 *  设备支持的翻转有哪些 , 程序在设备左转再左转之后打印出的日志是程序方向不变 , 即:设备倒向 , 程序不是倒向反而还是左向;
 
 */
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    //默认的是:UIInterfaceOrientationMaskAllButUpsideDown , 所以在屏幕倒向的时候程序仍然是左向;
    
    NSUInteger mask = [super supportedInterfaceOrientations];
    if (mask == UIInterfaceOrientationMaskAll) {
        NSLog(@"mask all");
    } else if (mask == UIInterfaceOrientationMaskAllButUpsideDown) {
        NSLog(@"mask all but upsidedown");
    }
    //支持全部方向:
    return UIInterfaceOrientationMaskAll;
    
}


#pragma mark - 防止设备旋转的过程中状态栏消失
- (BOOL)prefersStatusBarHidden {
    return NO;
}

#pragma mark - init
- (instancetype)init
{
    self = [super init];
    if (self) {
        //UIDeviceOrientationDidChangeNotification通知:
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationChanged:) name:UIDeviceOrientationDidChangeNotification object:nil];
        //UIApplicationDidChangeStatusBarOrientationNotification通知:
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarOrientationChanged:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
        //键盘变换通知:
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];
        _presentAnimation = [[PresentAnimation alloc] init];
        _dismissAnimation = [[DismissAnimation alloc] init];
    }
    return self;
}

#pragma mark - 生命周期
- (void)viewDidLoad {
    [super viewDidLoad];
    
    _button = [[UIButton alloc] init];
    [_button setBackgroundColor:[UIColor orangeColor]];
    _button.frame = CGRectMake(100, 100, 150, 50);
    [_button setTitle:@"Present Button" forState:UIControlStateNormal];
    [_button addTarget:self action:@selector(presentButtonClick:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:_button];
    
    
    _pushButton = [[UIButton alloc] init];
    [_pushButton setBackgroundColor:[UIColor orangeColor]];
    _pushButton.frame = CGRectMake(100, 200, 150, 50);
    [_pushButton setTitle:@"Push Button" forState:UIControlStateNormal];
    [_pushButton addTarget:self action:@selector(pushButtonClick:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:_pushButton];
    
    
    
    _textField = [[UITextField alloc] init];
    _textField.backgroundColor = [UIColor lightGrayColor];
    [_textField setText:@"text"];
    _textField.frame = CGRectMake(100, 300, 200, 30);
    [self.view addSubview:_textField];
}


#pragma mark - PUSH & POP 视图
- (void)pushButtonClick:(id)sender {
    //initWithFromPush:方法:
    TestSecondViewController *testSecondVC = [[TestSecondViewController alloc] initWithFromPush:YES];
    [self.navigationController pushViewController:testSecondVC animated:YES];
}


#pragma mark - 模态视图
- (void)presentButtonClick:(id)sender {
    TestSecondViewController *modalVC = [[TestSecondViewController alloc] init];
    modalVC.delegate = self;
    //三要素:
    //1.告诉系统我的转场动画要自定义而不是默认:
    modalVC.transitioningDelegate = self;
    //这个style控制着模态视图跳转到新视图后原来视图的内容是否保留的操作:默认 UIModalPresentationFullScreen 不保留 , 在这里选择 UIModalPresentationCustom 可以保留:
    modalVC.modalPresentationStyle = UIModalPresentationCustom;
    [self presentViewController:modalVC animated:YES completion:nil];
}



#pragma mark - 
- (void)testSecondViewControllerDidDismiss:(TestSecondViewController *)testSecondViewController {
    [self dismissViewControllerAnimated:YES completion:nil];
}


#pragma mark - 
//自定义转场代理方法:
- (id)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
    //因为返回值是 id 所以我要创建object对象去实现 UIViewControllerAnimatedTransitioning 即可!
    
    if ([presented isKindOfClass:[TestSecondViewController class]]) {
        return _presentAnimation;
    }
    return nil;
    
}

- (id)animationControllerForDismissedController:(UIViewController *)dismissed
{
    if ([dismissed isKindOfClass:[TestViewController class]]) {
        return _dismissAnimation;
    }
    return nil;
    
}


#pragma mark - 
- (void)deviceOrientationChanged:(NSNotification *)noti {
    NSLog(@"#1 receive device orientation changed notification");
    NSLog(@"%@", [self descriptionForDeviceOrientation:[UIDevice currentDevice].orientation]);
    NSLog(@"********************************************************************");
    NSLog(@"%@", [self descriptionForInterfaceOrientation:[UIApplication sharedApplication].statusBarOrientation]);
}

- (NSString *)descriptionForDeviceOrientation:(UIDeviceOrientation)deviceOrientation {
    switch (deviceOrientation) {
        case UIDeviceOrientationLandscapeLeft:
            return @"设备左向";
            break;
        case UIDeviceOrientationLandscapeRight:
            return @"设备右向";
            break;
        case UIDeviceOrientationPortraitUpsideDown:
            return @"设备倒向";
            break;
        case UIDeviceOrientationPortrait:
            return @"设备正向";
            break;
        default:
            break;
    }
    return @"其他方向";
    
}


- (NSString *)descriptionForInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    switch (interfaceOrientation) {
        case UIInterfaceOrientationLandscapeLeft:
            return @"程序左向";
            break;
        case UIInterfaceOrientationLandscapeRight:
            return @"程序右向";
            break;
        case UIInterfaceOrientationPortraitUpsideDown:
            return @"程序倒向";
            break;
        case UIInterfaceOrientationPortrait:
            return @"程序正向";
            break;
        default:
            break;
    }
    return @"其他方向";
    
}




#pragma mark - 
- (void)statusBarOrientationChanged:(NSNotification *)noti {
    NSLog(@"#2 receive interface orientation change");
    NSLog(@"%@", [self descriptionForDeviceOrientation:[UIDevice currentDevice].orientation]);
    NSLog(@"%@", [self descriptionForInterfaceOrientation:[UIApplication sharedApplication].statusBarOrientation]);
}


/**
 *  横屏 UI 布局 与 竖屏的 UI 布局不同,如何来做?!
 *  在屏幕发生旋转的时候重新进行layout布局即可! , 调用 viewWillTransitionToSize:方法;
 */
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator {
    [coordinator animateAlongsideTransition:^(id  _Nonnull context) {
        //正在旋转中...
        
    } completion:^(id  _Nonnull context) {
        //旋转完成!
        NSLog(@"screen bounds = %@" , NSStringFromCGRect([UIScreen mainScreen].bounds));
    }];
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
}


//屏幕旋转的过渡动画不够流畅 , 此时应该把 MainInterface 中的内 Main 删除即可!
- (void)viewDidLayoutSubviews {
    if (UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation)) {
        _button.frame = CGRectMake(100, 100, 150, 50);
        _pushButton.frame = CGRectMake(100, 200, 150, 50);
        _textField.frame = CGRectMake(100, 300, 200, 30);
    } else {
        _button.frame = CGRectMake(100, 100, 150, 50);
        _pushButton.frame = CGRectMake(100, 200, 150, 50);
        _textField.frame = CGRectMake(300, 100, 200, 30);
    }
}


#pragma mark - 键盘变换通知
- (void)keyboardDidShow:(NSNotification *)noti {
    //NSLog(@"keyboard show, user info %@", notification.userInfo);
    NSLog(@"keyboard show user info %@" , noti.userInfo);
}


/**
 *  强制横屏方法用transform:view.transform = CGAffineTransformMakeRotation(M_PI_2);
 *  但是用transform方法旋转的话需要进行隐藏statusBar操作 , 因为statusBar不会根据屏幕跟随旋转!
 *  UIDevice.orientation方法:
 *  NSNumber *number = [NSNumber numberWithInteger:UIDeviceOrientationPortrait];
 [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
 
 */

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    
}

@end

TestSecondViewController.h

#import 
@class TestSecondViewController;

@protocol TestSecondViewControllerDelegate 

- (void)testSecondViewControllerDidDismiss:(TestSecondViewController *)testSecondViewController;

@end

@interface TestSecondViewController : UIViewController

@property (nonatomic, weak) id delegate;

- (instancetype)initWithFromPush:(BOOL)fromPush;

@end

TestSecondViewController.m

#import "TestSecondViewController.h"

@interface TestSecondViewController ()
{
    UIButton *_button;
    BOOL _fromPush;
}
@end

@implementation TestSecondViewController


#pragma mark - init
- (instancetype)initWithFromPush:(BOOL)fromPush {
    self = [super init];
    if (self) {
        //在构造方法里记录一下,传进来的这个布尔值:
        _fromPush = fromPush;
    }
    return self;
}


- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor lightGrayColor];
    _button = [[UIButton alloc] init];
    _button.backgroundColor = [UIColor redColor];
    _button.frame = CGRectMake(100, 100, 150, 50);
    
    if (_fromPush) {
        [_button setTitle:@"Pop" forState:UIControlStateNormal];
    } else {
        [_button setTitle:@"Dismiss" forState:UIControlStateNormal];
    }
    [_button addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:_button];
    
}


#pragma mark - 按钮点击事件
- (void)buttonClicked:(id)sender {
    if (_fromPush) {
        [self.navigationController popViewControllerAnimated:YES];
    } else {
        if (_delegate && [_delegate respondsToSelector:@selector(testSecondViewControllerDidDismiss:)]) {
            [_delegate testSecondViewControllerDidDismiss:self];
        }
    }
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    
}

@end

PresentAnimation.h

#import 
#import 


@interface PresentAnimation : NSObject 

@end

PresentAnimation.m

#import "PresentAnimation.h"

@implementation PresentAnimation


#pragma mark - 
/**
 *  动画时长:
 */
- (NSTimeInterval)transitionDuration:(nullable id )transitionContext
{
    return 1.0f;
}

/**
 *  动画效果:
 */
- (void)animateTransition:(id )transitionContext
{
    //viewControllerKey:
    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    //viewKey:
    UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
    
    UIView *containerView = [transitionContext containerView];
    [containerView addSubview:toView];
    CGRect targetFrame = [transitionContext finalFrameForViewController:toVC];
    //偏移量的操作:
    //targetFrame = CGRectOffset(targetFrame, 0, 500);
    toView.frame = CGRectOffset(targetFrame, 0, -targetFrame.size.height);
    NSTimeInterval duration = [self transitionDuration:transitionContext];
    [UIView animateWithDuration:duration delay:0 usingSpringWithDamping:0.6 initialSpringVelocity:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
        
        toView.frame = targetFrame;
        
    } completion:^(BOOL finished) {
        //过场动画完成:
        [transitionContext completeTransition:YES];
        
    }];
    
}

@end

DismissAnimation.h

#import 
#import 

@interface DismissAnimation : NSObject 

@end

DismissAnimation.m

#import "DismissAnimation.h"

@implementation DismissAnimation

- (NSTimeInterval)transitionDuration:(id)transitionContext
{
    return 1;
}

- (void)animateTransition:(id)transitionContext
{
    UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
    
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
    
    UIView *containerView = [transitionContext containerView];
    [containerView addSubview:toView];
    [containerView sendSubviewToBack:toView];
    
    CGRect initFrame = [transitionContext initialFrameForViewController:fromVC];
    CGRect targetFrame = CGRectOffset(initFrame, 0, - initFrame.size.height);
    
    NSTimeInterval duration = [self transitionDuration:transitionContext];
    
    [UIView animateWithDuration:duration animations:^{
        
        fromView.frame = targetFrame;
        
    } completion:^(BOOL finished) {
        
        [transitionContext completeTransition:YES];
    }];
}

@end

CustomNavigationControllerDelegate.h

#import 
#import 

@interface CustomNavigationControllerDelegate : NSObject 

@property (nonatomic, weak) UINavigationController *navigationController;

@end

CustomNavigationControllerDelegate.m

#import "CustomNavigationControllerDelegate.h"
#import "TransformAnimation.h"
#import "PresentAnimation.h"

@interface CustomNavigationControllerDelegate ()

{
    UIPercentDrivenInteractiveTransition *_innerPercentTransition;
    
    BOOL _underInteracting;
}

@end

@implementation CustomNavigationControllerDelegate

- (nullable id )navigationController:(UINavigationController *)navigationController
                                            animationControllerForOperation:(UINavigationControllerOperation)operation
                                                         fromViewController:(UIViewController *)fromVC
                                                           toViewController:(UIViewController *)toVC  NS_AVAILABLE_IOS(7_0)
{
    if (operation == UINavigationControllerOperationPop)
    {
        return [[TransformAnimation alloc] init];
    } else if (UINavigationControllerOperationPush)
    {
        return [[PresentAnimation alloc] init];
    }
    return nil;
}


#pragma mark - 交互式转场
- (nullable id )navigationController:(UINavigationController *)navigationController
                                   interactionControllerForAnimationController:(id ) animationController
{
    if (_underInteracting)
    {
        return _innerPercentTransition;
    }
    
    return nil;
}

//setter方法:
- (void)setNavigationController:(UINavigationController *)navigationController
{
    _navigationController = navigationController;
    
    UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
    [_navigationController.view addGestureRecognizer:panGesture];
}


#pragma mark - 手势
- (void)pan:(id)sender
{
    if ([sender isKindOfClass:[UIPanGestureRecognizer class]])
    {
        UIPanGestureRecognizer *panGesture = sender;
        
        switch (panGesture.state) {
            case UIGestureRecognizerStateBegan:
            {
                _underInteracting = YES;
                
                _innerPercentTransition = [[UIPercentDrivenInteractiveTransition alloc] init];
                
                if ([_navigationController.viewControllers count] > 1)
                {
                    [_navigationController popViewControllerAnimated:YES];
                }
                break;
            }
                
            case UIGestureRecognizerStateChanged:
            {
                CGPoint translationPoint = [panGesture translationInView:_navigationController.view];
                CGFloat progress = translationPoint.x / CGRectGetWidth(_navigationController.view.bounds);
                
                [_innerPercentTransition updateInteractiveTransition:progress];
                
                
                break;
            }
            case UIGestureRecognizerStateEnded:
            {
                _underInteracting = NO;
                
                if ([panGesture velocityInView:_navigationController.view].x > 0) {
                    [_innerPercentTransition finishInteractiveTransition];
                }
                else
                {
                    [_innerPercentTransition cancelInteractiveTransition];
                }
                _innerPercentTransition = nil;
                break;
            }
                
            default:
                [_innerPercentTransition cancelInteractiveTransition];
                _innerPercentTransition = nil;
                break;
        }
    }
}

@end

TransformAnimation.h

#import 
#import 

@interface TransformAnimation : NSObject 

@end

TransformAnimation.m

#import "TransformAnimation.h"

@implementation TransformAnimation

- (NSTimeInterval)transitionDuration:(nullable id )transitionContext
{
    return 1.0f;
}

- (void)animateTransition:(id )transitionContext
{
    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    [[transitionContext containerView] addSubview:toVC.view];
    toVC.view.alpha = 0;
    [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
        fromVC.view.transform = CGAffineTransformMakeScale(0.1, 0.1);
        toVC.view.alpha = 1;
    } completion:^(BOOL finished) {
        //还原fromVC:
        fromVC.view.transform = CGAffineTransformIdentity;
        //动画结束后,完成上下文:
        [transitionContext completeTransition:YES]; 
    }];
}

@end

愿编程让这个世界更美好

你可能感兴趣的:(自定义转场)