1. 程式人生 > >IOS 自定義push和pop動畫

IOS 自定義push和pop動畫

轉載大神    
http://www.cocoachina.com/ios/20150401/11459.html

自iOS7之後,引進了新的API來構造UIViewController之間的轉場動畫,經過幾天的研究,終於做出了一個小Damo,來粗淺談談。

這幾個API如下:

<1.>UIViewControllerAnimatedTransitioning   動畫協議

<2>.UIViewControllerInteractiveTransitioning  互動協議

<3>.UIViewControllerContextTransitioning      上下文協議
<4>.UIPercentDrivenInteractiveTransition         遵守     <2>協議的一個官方類

之所以官方給出的API是協議而不是類別,給出的說法是為了靈活性,你可以在ViewController裡面直接寫,也可以直接另寫一個類封裝起來。

進入正文:

1、  這個類負責動畫,繼承自NSObject,遵守UIViewControllerAnimatedTransitioning協議,記得匯入UIKit,如下
@interface PopAnimation : NSObject <UIViewControllerAnimatedTransitioning>

@end

在.m檔案中實現協議其中的兩個方法:

   (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
//這個方法返回動畫執行的時間
return 0.25;

}

/**
 transitionContext你可以看作是一個工具,用來獲取一系列動畫執行相關的物件,並且通知系統動畫是否完成等功能。
*/
 (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
/**
   獲取動畫來自的那個控制器
 */
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
/**
  獲取轉場到的那個控制器
 */
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

/**
   轉場動畫是兩個控制器檢視時間的動畫,需要一個containerView來作為一個“舞臺”,讓動畫執行。
 */
UIView *containerView = [transitionContext containerView];
[containerView insertSubview:toViewController.view belowSubview:fromViewController.view];

NSTimeInterval duration = [self transitionDuration:transitionContext];

//    /**
//     *  執行動畫,我們讓fromVC的檢視移動到螢幕最右側
//     */
//    [UIView animateWithDuration:duration  animations:^{
//        fromViewController.view.transform = CGAffineTransformMakeTranslation([UIScreen mainScreen].bounds.size.width, 0);
}completion:^(BOOL finished) {
   /**
//         *  當你的動畫執行完成,這個方法必須要呼叫,否則系統會認為你的其餘任何操作都在動畫執行過程中。
//         */
//        [transitionContext completeTransition:!transitionContext.transitionWasCancelled];
//    }];

_transitionContext = transitionContext;
//    ----------------pop動畫一-------------------------//

[UIView beginAnimations:@"View Flip" context:nil];
[UIView setAnimationDuration:duration];
[UIView setAnimationDelegate:self];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:containerView cache:YES];
[UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:)];
[UIView commitAnimations];//提交UIView動畫
[containerView exchangeSubviewAtIndex:0 withSubviewAtIndex:1];

//    //----------------pop動畫二-------------------------//
//    
//    CATransition *tr = [CATransition animation];
//    tr.type = @"cube";
//    tr.subtype = @"fromLeft";
//    tr.duration = duration;
//    tr.removedOnCompletion = NO;
//    tr.fillMode = kCAFillModeForwards;
//    tr.delegate = self;
//    [containerView.layer addAnimation:tr forKey:nil];
//    [containerView exchangeSubviewAtIndex:0 withSubviewAtIndex:1];

}
*在pop過程中可使用動畫,例如

_transitionContext = transitionContext;

//   ----------pop動畫一-------------------------//

[UIView beginAnimations:@"View Flip" context:nil];

[UIView setAnimationDuration:duration];

[UIView setAnimationDelegate:self];

[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:containerView cache:YES];

[UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:)];

[UIView commitAnimations];//提交UIView動畫

[containerView exchangeSubviewAtIndex:0 withSubviewAtIndex:1];

 Push基類.m

第一個方法返回的是動畫時間,不做多言。

第二個方法是動畫的具體執行,方法的引數transitionContext遵守了UIViewControllerContextTransitioning協議,所以它包含了許多關於專場所需要的內容,包括轉入ViewController和轉出Viewcontroller,還有動畫容器View--containerView等。

我們點進去UIViewControllerContextTransitioning協議,可以找到許多的屬性和方法,這些方法中最重要的幾個方法和意義如下:

   (UIView*)containerView;  //獲取容器View                                               

 (void)completeTransition:(BOOL)didComplete;//通過此引數獲知動畫是否結束                     

(UIViewController*)viewControllerForKey:(NSString*)key;  //獲取轉入、轉出VC

(CGRect)initialFrameForViewController:(UIViewController*)vc   //獲取動畫前VC的frame

 (CGRect)finalFrameForViewController:(UIViewController*)vc;    //獲取動畫後VC的frame

(push的基類也和pop一樣,只是具體動畫效果程式碼的不同)

另外,我們需要返回一個遵守了UIViewControllerInteractiveTransitioning協議的物件(提示一下,這兩個協議容易混淆,要注意區分,一個是負責動畫,一個是負責互動過程),蘋果已經有一個類專門處理這個功能,它叫UIPercentDrivenInteractiveTransition,當然你也可以自定義一個這樣的類。我們可以這樣理解它的作用:前面在方法1中返回的動畫,會在執行的過程中被系統分解以用於使用者互動,這個互動過程的動畫完成度就由它來調控。下面我們來看一下如何使用它。(為了讓控制器檢視拖動,我們給控制器的檢視加了一個拖動手勢,在拖動方法裡我們對這個物件進行操作)
定義一個類,NavigationInteractiveTransition ,實現
.h中
@class UIViewController, UIPercentDrivenInteractiveTransition;
@interface NavigationInteractiveTransition : NSObject <UINavigationControllerDelegate>
(instancetype)initWithViewController:(UIViewController *)vc;
(void)handleControllerPop:(UIPanGestureRecognizer *)recognizer;
(UIPercentDrivenInteractiveTransition *)interactivePopTransition;
@end
.m中
設定導航控制器的delegate為這個自定義的類

@interface NavigationInteractiveTransition ()
@property (nonatomic, weak) UINavigationController *vc;
@property (nonatomic, strong) UIPercentDrivenInteractiveTransition *interactivePopTransition;
@end

@implementation NavigationInteractiveTransition

 (instancetype)initWithViewController:(UIViewController *)vc
{
self = [super init];
if (self) {
    self.vc = (UINavigationController *)vc;
    self.vc.delegate = self;
}
return self;
}

新增手勢代理
/*
  我們把使用者的每次Pan手勢操作作為一次pop動畫的執行
 */
(void)handleControllerPop:(UIPanGestureRecognizer *)recognizer {
/*
   interactivePopTransition就是我們說的方法2返回的物件,我們需要更新它的進度來控制Pop動畫的流程,我們用手指在檢視中的位置與檢視寬度比例作為它的進度。
 */
CGFloat progress = [recognizer translationInView:recognizer.view].x / recognizer.view.bounds.size.width;
/**
   穩定進度區間,讓它在0.0(未完成)~1.0(已完成)之間
 */
progress = MIN(1.0, MAX(0.0, progress));
if (recognizer.state == UIGestureRecognizerStateBegan) {
    /**
       手勢開始,新建一個監控物件
     */
    self.interactivePopTransition = [[UIPercentDrivenInteractiveTransition alloc] init];
    /**
       告訴控制器開始執行pop的動畫
     */
    [self.vc popViewControllerAnimated:YES];
}
else if (recognizer.state == UIGestureRecognizerStateChanged) {

    /**
       更新手勢的完成進度
     */
    [self.interactivePopTransition updateInteractiveTransition:progress];
}
else if (recognizer.state == UIGestureRecognizerStateEnded || recognizer.state == UIGestureRecognizerStateCancelled) {

    /**
       手勢結束時如果進度大於一半,那麼就完成pop操作,否則重新來過。
     */
    if (progress > 0.3) {
        [UIView animateWithDuration:0.3 animations:^{
            [self.interactivePopTransition finishInteractiveTransition];
        }completion:^(BOOL finished) {
            self.interactivePopTransition = nil;
        }];

    }
    else {
        [UIView animateWithDuration:0.3 animations:^{
            [self.interactivePopTransition cancelInteractiveTransition];
        }completion:^(BOOL finished) {
            self.interactivePopTransition = nil;
        }];
    }


}

}

   然後初始化pop動畫類
    (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
                              animationControllerForOperation:(UINavigationControllerOperation)operation
                                           fromViewController:(UIViewController *)fromVC
                                             toViewController:(UIViewController *)toVC {
/**
  方法1中判斷如果當前執行的是Pop操作,就返回我們自定義的Pop動畫物件。
 */
if (operation == UINavigationControllerOperationPop)
    return [[PopAnimation alloc] init];

return nil;
}

最後,自定義一個整合UINavigationcontroller的類來使用
#import "NavigationInteractiveTransition.h"

@interface Nav () <UIGestureRecognizerDelegate>
@property (nonatomic, weak) UIPanGestureRecognizer *popRecognizer;
 /**
 方案一不需要的變數
 */
@property (nonatomic, strong) NavigationInteractiveTransition *navT;
@end

@implementation Nav

(void)viewDidLoad {
[super viewDidLoad];

UIGestureRecognizer *gesture = self.interactivePopGestureRecognizer;
gesture.enabled = NO;
UIView *gestureView = gesture.view;

UIPanGestureRecognizer *popRecognizer = [[UIPanGestureRecognizer alloc] init];
popRecognizer.delegate = self;
popRecognizer.maximumNumberOfTouches = 1;
[gestureView addGestureRecognizer:popRecognizer];

_navT = [[NavigationInteractiveTransition alloc] initWithViewController:self];
[popRecognizer addTarget:_navT action:@selector(handleControllerPop:)];
}

 (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
/**
   這裡有兩個條件不允許手勢執行,1、當前控制器為根控制器;2、如果這個push、pop動畫正在執行(私有屬性)
 */
return self.viewControllers.count != 1 && ![[self valueForKey:@"_isTransitioning"] boolValue];
}

接下來就可以使用了,push和pop的方法一樣,只是在例項化的時候注意區分
(id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
                              animationControllerForOperation:(UINavigationControllerOperation)operation
                                           fromViewController:(UIViewController *)fromVC
                                             toViewController:(UIViewController *)toVC {
/**
  方法1中判斷如果當前執行的是Pop操作,就返回我們自定義的Pop動畫物件。
 */
if (operation == UINavigationControllerOperationPop)
    return [[PopAnimation alloc] init];
else if (operation == UINavigationControllerOperationPush)
return [[PushAnimation alloc] init];
return nil;
}

常見問題:

   經常會遇到,橫向翻頁的scrollview和滑動手勢衝突,那麼

1.首先自定義一個scrollView,比如:CustomScrollView,遵守協議,然後在實現檔案中寫如下程式碼:


-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer

{

// 首先判斷otherGestureRecognizer是不是系統pop手勢

if ([otherGestureRecognizer.view isKindOfClass:NSClassFromString(@"UILayoutContainerView")]) {

// 再判斷系統手勢的state是began還是fail,同時判斷scrollView的位置是不是正好在最左邊

if (otherGestureRecognizer.state == UIGestureRecognizerStateBegan && self.contentOffset.x == 0) {

return YES;

}

}

 return NO;

}

2.那個橫向滾動的scrollView繼承這個自定義scrollView,也就是CustomScrollView

相關推薦

IOS 定義pushpop動畫

轉載大神 http://www.cocoachina.com/ios/20150401/11459.html 自iOS7之後,引進了新的API來構造UIViewController之間的轉場動畫,經過幾天的研究,終於做出了一個小Damo,來粗淺談談。

Swift基礎之定義PUSHPOP跳轉動畫

之前用OC程式碼寫過PUSH和POP的轉場動畫,閒來無事,將其轉換成Swift語言,希望對大家有幫助,轉載請註明。。。。如何實現PUSH和POP的轉場動畫?首先,建立一個NSObject的類,分別用來實現PUSH和POP的動畫效果建立PUSH檔案,實現扇形效果,程式碼如下:需

iOS定義轉場動畫(1)——定義Push轉場動畫

版本:Xcode 7.0.1 語言:Objective-C 轉場動畫就是viewController之間切換的動畫。 主要有以下三種自定義方法: 列Push & Pop Modal Segue 第一種是UINavigationControl

iOS pushViewController 實現push pop 預設動畫效果

一、push預設動畫效果    CATransition *transition = [CATransitionanimation];     transition.duration = 0.3f;     transition.timingFunction = [CAM

iOS 定義雷達 掃描/擴散動畫 View

前段時間 沒事兒,自己自定義了 一個雷達掃描/擴散效果的View。 掃描View 效果如下: 擴散View 效果如下: 自定義的程式碼如下: 1. RadarView.h #import <UIKit/UIKit.h> typedef NS_ENUM(

iOS 定義日期資料來源選擇控制元件

需求 App開發中經常會有日期選擇(如生日、睡眠定時等)或者省市區選擇等此類功能,通常UI中不會單獨使用UI中的控制元件,而是在UIPickerView的基礎上增加一個取消和確定按鈕 特點 支援常見的選擇型的資料格式 該控制元件集成了 yyyy-M

iOS 定義導航欄pop返回手勢,全屏右滑返回

- (void)viewDidLoad {     [superviewDidLoad]; //重新給interactivePopGestureRecognizer.delegate掛代理

iOS定義轉場動畫

http://www.jianshu.com/p/45434f73019e 更新,更簡單的自定義轉場整合! 寫在前面 這兩天閒下來好好的研究了一下自定義轉場,關於這方面的文章網路上已經很多了,作為新手,我想通過這篇文章把自己這幾天的相關學習心得記錄一下,方便加深印響和

iOS xcode 生成 定義註釋API

//聯絡人:石虎 QQ:1224614774 暱稱:嗡嘛呢叭咪哄 一、概念  Objective-C寫程式碼時有三種可能的方法來標示一個註釋文件區域:   1. 把你的註釋包含在/** –

IOS百度地圖定義大頭針氣泡

文/煜寒了(簡書作者) 原文連結:http://www.jianshu.com/p/6a334f071c69 著作權歸作者所有,轉載請聯絡作者獲得授權,並標註“簡書作者”。1.首先實現新增多個標註和自定義氣泡 新增自定義標註 [_mapView addAnnotations:array]; arry 中放入標

iOS 定義載入等待動畫

一般來說,我們的專案中請求網路資料是一個比較耗時的操作,在請求的過程中如果給使用者只展示空白的頁面或者預設的頁面,難免顯得有些單調,這個時候我們可以新增一個指示動畫,開始請求的時候執行動畫,資料請求下來了停止動畫,這樣使用者體驗會好一些。下面開始自定義我們自己的載入指示動畫

定義dialog彈出dialog的動畫

自定義dialog final Dialog dialog = new Dialog(ChartVisit.this); LayoutInflater inflater=getLayoutInflater();

【轉】定義presentviewcontrollerpushviewcontroller轉場動畫

自定義NavigationController動畫 首先,實現一個非常簡單的UINavigationController轉場,一般會這麼幹 實現FirstViewController,加到Window上(沒用storyboard和xib) 實現FirstViewCo

IOS 定義導航欄標籤欄

大多數ios應用都是以標籤欄加導航欄的形式呈現,一般根控制器都是UIToolbar,然後再以UINavigationController為子控制器,然後再加入UIViewController。為什麼要這樣?因為不同的標籤欄的項一般對應不同的功能,那麼導航欄的標題一般就不同。

iOS定義tabbar後popToRootViewContrillerpoptoviewcontroller時出現兩個tabbar 的解決辦法

iOS自定義tabbar後popToRootViewContriller和poptoviewcontroller時出現兩個tabbar 的解決辦法 問題:iOS自定義tabbar後popToRootViewContriller和poptoviewcontroller時出

iOS定義轉場動畫(4)——定義模態跳轉之dismiss與手勢驅動

Dismiss 效果: 1、新建PresentTransition繼承NSObject,並在.h中遵守UIViewControllerAnimatedTransitioning協議。 2、實現協議的兩個方法,並在其中編寫 Push 的動畫。類似Pr

iOS定義cell控制器之間常用傳值方式

自定義cell和控制器之間常用傳值方式 1.代理傳值(用的多) 在自定義cell的.h檔案中制定協議,例: @class OtherTableViewCell; @protocol OtherTableViewCellDelegate <NSObject&g

ios 定義鍵盤的return鍵以及鍵盤的其他一些屬性

variable 位置 arch ext ddr gin character 觸發 hone //初始化textfield並設置位置及大小 UITextField *text = [[UITextField alloc]initWithFrame:CGRectMake(20

IOS 定義按鈕(代碼實現)+九宮格

uifont 排列 end uiview height iyu void rec name 在一些下載應用裏整個頁面都是按鈕,有好多好多,但是仔細觀察不難發現他們很有規律。就像下面一樣?? 很有規律的排列在屏幕上,那麽這需要我們怎麽去做能。 正如標題,我們需要了解兩個知

iOS定義一些提示控件

mat cat get -1 from start mask ins sel 代碼如下: .h中的代碼: // // HKUIToolsView.h // HKUIToolsDemo // // Created by isHakan on 2017/7/28