iOS_20_微博自訂可動畫切換的導航控制器
最終效果:
AnimatedNavigationController.h
//// AnimatedNavigationController.h// 20_帥哥no微博//// Created by beyond on 14-8-10.// Copyright (c) 2014年 com.beyond. All rights reserved.// 繼承自導航控制器,但是多了一個功能,可以監聽手勢,進行動畫切換#import @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 #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都平移 // 挪動整個導航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