iOS_20_微博自定义可动画切换的导航控制器

来源:互联网 发布:中国网络什么时候墙的 编辑:程序博客网 时间:2024/04/30 23:15

最终效果:





AnimatedNavigationController.h

////  AnimatedNavigationController.h//  20_帅哥no微博////  Created by beyond on 14-8-10.//  Copyright (c) 2014年 com.beyond. All rights reserved.//  继承自导航控制器,但是多了一个功能,可以监听手势,进行动画切换#import <UIKit/UIKit.h>@interface AnimatedNavigationController : UINavigationController@end
AnimatedNavigationController.m
////  AnimatedNavigationController.m//  20_帅哥no微博////  Created by beyond on 14-8-10.//  Copyright (c) 2014年 com.beyond. All rights reserved.//#import "AnimatedNavigationController.h"// 截图用到#import <QuartzCore/QuartzCore.h>#import "BeyondViewController.h"@interface AnimatedNavigationController (){    // 屏幕截图    UIImageView *_screenshotImgView;    // 截图上面的黑色半透明遮罩    UIView *_coverView;        // 存放所有截图    NSMutableArray *_screenshotImgs;}@end@implementation AnimatedNavigationController- (void)viewDidLoad{    [super viewDidLoad];    // 1,创建Pan手势识别器,并绑定监听方法    UIPanGestureRecognizer *panGestureRec = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(panGestureRec:)];    // 为导航控制器的view添加Pan手势识别器    [self.view addGestureRecognizer:panGestureRec];                // 2.创建截图的ImageView    _screenshotImgView = [[UIImageView alloc] init];    // app的frame是除去了状态栏高度的frame    _screenshotImgView.frame = [UIScreen mainScreen].applicationFrame;    //(0 20; 320 460);        // 3.创建截图上面的黑色半透明遮罩    _coverView = [[UIView alloc] init];    // 遮罩的frame就是截图的frame    _coverView.frame = _screenshotImgView.frame;    // 遮罩为黑色    _coverView.backgroundColor = [UIColor blackColor];        // 4.存放所有的截图数组初始化    _screenshotImgs = [NSMutableArray array];            }// 重写push方法,在push之前 先截取图片- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{    // 只有在导航控制器里面有子控制器的时候才需要截图    if (self.viewControllers.count >= 1) {        // 调用自定义方法,使用上下文截图        [self screenShot];    }    // 截图完毕之后,才调用父类的push方法    [super pushViewController:viewController animated:YES];}// 使用上下文截图,并使用指定的区域裁剪,模板代码- (void)screenShot{    // 将要被截图的view,即窗口的根控制器的view(必须不含状态栏,默认ios7中控制器是包含了状态栏的)    BeyondViewController *beyondVC = (BeyondViewController *)self.view.window.rootViewController;    // 背景图片 总的大小    CGSize size = beyondVC.view.frame.size;    // 开启上下文,使用参数之后,截出来的是原图(YES  0.0 质量高)    UIGraphicsBeginImageContextWithOptions(size, YES, 0.0);    // 要裁剪的矩形范围    CGRect rect = CGRectMake(0, -20.8, size.width, size.height + 20 );    //注:iOS7以后renderInContext:由drawViewHierarchyInRect:afterScreenUpdates:替代    [beyondVC.view drawViewHierarchyInRect:rect  afterScreenUpdates:NO];    // 从上下文中,取出UIImage    UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();    // 添加截取好的图片到图片数组    [_screenshotImgs addObject:snapshot];        // 千万记得,结束上下文(移除栈顶的基于当前位图的图形上下文)    UIGraphicsEndImageContext();    }// 监听手势的方法,只要是有手势就会执行- (void)panGestureRec:(UIPanGestureRecognizer *)panGestureRec{        // 如果当前显示的控制器已经是根控制器了,不需要做任何切换动画,直接返回    if(self.topViewController == self.viewControllers[0]) return;    // 判断pan手势的各个阶段    switch (panGestureRec.state) {        case UIGestureRecognizerStateBegan:            // 开始拖拽阶段            [self dragBegin];            break;                    case UIGestureRecognizerStateEnded:            // 结束拖拽阶段            [self dragEnd];            break;                    default:            // 正在拖拽阶段            [self dragging:panGestureRec];            break;    }}#pragma mark 开始拖动,添加图片和遮罩- (void)dragBegin{    // 重点,每次开始Pan手势时,都要添加截图imageview 和 遮盖cover到window中    [self.view.window insertSubview:_screenshotImgView atIndex:0];    [self.view.window insertSubview:_coverView aboveSubview:_screenshotImgView];        // 并且,让imgView显示截图数组中的最后(最新)一张截图    _screenshotImgView.image = [_screenshotImgs lastObject];}// 默认的将要进行缩放的截图的初始比例#define kDefaultScale 0.6// 默认的将要变透明的遮罩的初始透明度(全黑)#define kDefaultAlpha 1.0// 当拖动的距离,占了屏幕的总宽度的3/4时, 就让imageview完全显示,遮盖完全消失#define kTargetTranslateScale 0.75#pragma mark 正在拖动,动画效果的精髓,进行缩放和透明度变化- (void)dragging:(UIPanGestureRecognizer *)pan{    // 得到手指拖动的位移    CGFloat offsetX = [pan translationInView:self.view].x;    // 只允许往右边拖,禁止向左拖    if (offsetX < 0) offsetX = 0;    // 让整个导航的view都平移         self.view.transform = CGAffineTransformMakeTranslation(offsetX, 0);        // 计算目前手指拖动位移占屏幕总的宽度的比例,当这个比例达到3/4时, 就让imageview完全显示,遮盖完全消失    double currentTranslateScaleX = offsetX/self.view.frame.size.width;        // 让imageview缩放,默认的比例+(当前平移比例/目标平移比例)*(1-默认的比例)    double scale = kDefaultScale + (currentTranslateScaleX/kTargetTranslateScale) * (1 - kDefaultScale);    // 已经达到原始大小了,就可以了,不用放得更大了    if (scale > 1) scale = 1;    _screenshotImgView.transform = CGAffineTransformMakeScale(scale, scale);        // 让遮盖透明度改变,直到减为0,让遮罩完全透明,默认的比例-(当前平移比例/目标平移比例)*默认的比例    double alpha = kDefaultAlpha - (currentTranslateScaleX/kTargetTranslateScale) * kDefaultAlpha;    _coverView.alpha = alpha;}#pragma mark 结束拖动,判断结束时拖动的距离作相应的处理,并将图片和遮罩从父控件上移除- (void)dragEnd{    // 取出挪动的距离    CGFloat translateX = self.view.transform.tx;    // 取出宽度    CGFloat width = self.view.frame.size.width;        if (translateX <= width * 0.5) {        // 如果手指移动的距离还不到屏幕的一半,往左边挪 (弹回)        [UIView animateWithDuration:0.3 animations:^{            // 重要~~让被右移的view弹回归位,只要清空transform即可办到            self.view.transform = CGAffineTransformIdentity;            // 让imageView大小恢复默认的scale            _screenshotImgView.transform = CGAffineTransformMakeScale(kDefaultScale, kDefaultScale);            // 让遮盖的透明度恢复默认的alpha 1.0            _coverView.alpha = kDefaultAlpha;        } completion:^(BOOL finished) {            // 重要,动画完成之后,每次都要记得 移除两个view,下次开始拖动时,再添加进来            [_screenshotImgView removeFromSuperview];            [_coverView removeFromSuperview];        }];    } else {        // 如果手指移动的距离还超过了屏幕的一半,往右边挪        [UIView animateWithDuration:0.3 animations:^{            // 让被右移的view完全挪到屏幕的最右边,结束之后,还要记得清空view的transform            self.view.transform = CGAffineTransformMakeTranslation(width, 0);            // 让imageView缩放置为1            _screenshotImgView.transform = CGAffineTransformMakeScale(1, 1);            // 让遮盖alpha变为0,变得完全透明            _coverView.alpha = 0;        } completion:^(BOOL finished) {            // 重要~~让被右移的view完全挪到屏幕的最右边,结束之后,还要记得清空view的transform,不然下次再次开始drag时会出问题,因为view的transform没有归零            self.view.transform = CGAffineTransformIdentity;            // 移除两个view,下次开始拖动时,再加回来            [_screenshotImgView removeFromSuperview];            [_coverView removeFromSuperview];                        // 执行正常的Pop操作:移除栈顶控制器,让真正的前一个控制器成为导航控制器的栈顶控制器            [self popViewControllerAnimated:NO];                        // 重要~记得这时候,可以移除截图数组里面最后一张没用的截图了            [_screenshotImgs removeLastObject];        }];    }}@end





1 0
原创粉丝点击