欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

iOS開發(fā)中的ViewController轉(zhuǎn)場(chǎng)切換效果實(shí)現(xiàn)簡(jiǎn)介

 更新時(shí)間:2015年09月10日 09:46:37   作者:TommyYaphetS  
這篇文章主要介紹了iOS開發(fā)中的ViewController轉(zhuǎn)場(chǎng)切換效果實(shí),主要針對(duì)iOS7以后新加入的API進(jìn)行講解,需要的朋友可以參考下

在iOS7之前,View Controller的切換主要有4種:

  1. Push/Pop,NavigationViewController
  2. Present and dismis Modal
  3. UITabBarController
  4. addChildViewController(一般用于自定義的繼承于 UIViewController 的容器子類)

iOS5,調(diào)用- (void)transitionFromViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion NS_AVAILABLE_IOS(5_0);

(1)前面3種方法這里就不多說了,很常見的系統(tǒng)方法.至于第四種,我在前面文章-剖析網(wǎng)易標(biāo)簽欄的效果中已經(jīng)做了闡述,但是它提供的容器轉(zhuǎn)場(chǎng)動(dòng)畫只可以實(shí)現(xiàn)一些簡(jiǎn)單的UIView動(dòng)畫,但是難以重用,耦合高.
(2)關(guān)鍵的API:
A.動(dòng)畫控制器 (Animation Controllers) 遵從 UIViewControllerAnimatedTransitioning 協(xié)議,并且負(fù)責(zé)實(shí)際執(zhí)行動(dòng)畫。
B.交互控制器 (Interaction Controllers) 通過遵從 UIViewControllerInteractiveTransitioning 協(xié)議來控制可交互式的轉(zhuǎn)場(chǎng)。
C.轉(zhuǎn)場(chǎng)代理 (Transitioning Delegates) 根據(jù)不同的轉(zhuǎn)場(chǎng)類型方便的提供需要的動(dòng)畫控制器和交互控制器。
   其中UINavigationControllerDelegate delegate 中新增了2個(gè)方法給NavigationController
               UIViewControllerTransitioningDelegate 新增transitioningDelegate  給Modal的Present和Dismis

復(fù)制代碼 代碼如下:

  UITabBarControllerDelegate delegate
- (id <UIViewControllerInteractiveTransitioning>)tabBarController:(UITabBarController *)tabBarController
interactionControllerForAnimationController: (id <UIViewControllerAnimatedTransitioning>)animationController NS_AVAILABLE_IOS(7_0);
- (id <UIViewControllerAnimatedTransitioning>)tabBarController:(UITabBarController *)tabBarController
animationControllerForTransitionFromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC NS_AVAILABLE_IOS(7_0);

D.轉(zhuǎn)場(chǎng)上下文 (Transitioning Contexts) 定義了轉(zhuǎn)場(chǎng)時(shí)需要的元數(shù)據(jù),比如在轉(zhuǎn)場(chǎng)過程中所參與的視圖控制器和視圖的相關(guān)屬性。 轉(zhuǎn)場(chǎng)上下文對(duì)象遵從 UIViewControllerContextTransitioning 協(xié)議,并且這是由系統(tǒng)負(fù)責(zé)生成和提供的。
E.轉(zhuǎn)場(chǎng)協(xié)調(diào)器(Transition Coordinators) 可以在運(yùn)行轉(zhuǎn)場(chǎng)動(dòng)畫時(shí),并行的運(yùn)行其他動(dòng)畫。 轉(zhuǎn)場(chǎng)協(xié)調(diào)器遵從 UIViewControllerTransitionCoordinator 協(xié)議。
(3)新的API主要提供了2種VC切換的方式:
A.非交互式切換,即定義一種從一個(gè)VC到另一個(gè)VC的動(dòng)畫效果,切換的時(shí)候自動(dòng)播放,
B.交互式切換,這種方式同樣需要定義動(dòng)畫效果,只是這個(gè)動(dòng)畫效果會(huì)根據(jù)跟隨交互式手勢(shì)來切換VC并同時(shí)播放動(dòng)畫效果。iOS7提供了一個(gè)默認(rèn)的基于百分比的動(dòng)畫實(shí)現(xiàn) UIPercentDrivenInteractiveTransition,而且根據(jù)WWDC的說明,最簡(jiǎn)單的實(shí)現(xiàn)交互式動(dòng)畫的方法就是通過繼承 UIPercentDrivenInteractiveTransition。

蘋果給我們開發(fā)者提供的是都是協(xié)議接口,所以我們能夠很好的單獨(dú)提出來寫成一個(gè)個(gè)類,在里面實(shí)現(xiàn)我們各種自定義效果.
(4)來看看實(shí)現(xiàn)UIViewControllerAnimatedTransitioning的自定義動(dòng)畫類

復(fù)制代碼 代碼如下:
/**
 *  自定義的動(dòng)畫類
 *  實(shí)現(xiàn)協(xié)議------>@protocol UIViewControllerAnimatedTransitioning
 *  這個(gè)接口負(fù)責(zé)切換的具體內(nèi)容,也即“切換中應(yīng)該發(fā)生什么”
 */ 
@interface MTHCustomAnimator : NSObject <UIViewControllerAnimatedTransitioning> 
 
@end 
 
@implementation MTHCustomAnimator 
 
// 系統(tǒng)給出一個(gè)切換上下文,我們根據(jù)上下文環(huán)境返回這個(gè)切換所需要的花費(fèi)時(shí)間 
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext 

    return 1.0; 

 
// 完成容器轉(zhuǎn)場(chǎng)動(dòng)畫的主要方法,我們對(duì)于切換時(shí)的UIView的設(shè)置和動(dòng)畫都在這個(gè)方法中完成 
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext 

    // 可以看做為destination ViewController 
    UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; 
    // 可以看做為source ViewController 
    UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; 
    // 添加toView到容器上 
        // 如果是XCode6 就可以用這段 
    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) 
    { 
        // iOS8 SDK 新API 
        UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey]; 
        //UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey]; 
        [[transitionContext containerView] addSubview:toView]; 
    }else{ 
        // 添加toView到容器上 
        [[transitionContext containerView] addSubview:toViewController.view]; 
    } 
     
    // 如果是XCode5 就是用這段 
    [[transitionContext containerView] addSubview:toViewController.view]; 
    toViewController.view.alpha = 0.0; 
    [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{ 
        // 動(dòng)畫效果有很多,這里就展示個(gè)左偏移 
        fromViewController.view.transform = CGAffineTransformMakeTranslation(-320, 0); 
        toViewController.view.alpha = 1.0; 
    } completion:^(BOOL finished) { 
        fromViewController.view.transform = CGAffineTransformIdentity; 
        // 聲明過渡結(jié)束-->記住,一定別忘了在過渡結(jié)束時(shí)調(diào)用 completeTransition: 這個(gè)方法 
        [transitionContext completeTransition:![transitionContext transitionWasCancelled]]; 
    }]; 

       PS:從協(xié)議中兩個(gè)方法可以看出,上面兩個(gè)必須實(shí)現(xiàn)的方法需要一個(gè)轉(zhuǎn)場(chǎng)上下文參數(shù),這是一個(gè)遵從UIViewControllerContextTransitioning 協(xié)議的對(duì)象。通常情況下,當(dāng)我們使用系統(tǒng)的類時(shí),系統(tǒng)框架為我們提供的轉(zhuǎn)場(chǎng)代理(Transitioning Delegates),為我們創(chuàng)建了轉(zhuǎn)場(chǎng)上下文對(duì)象,并把它傳遞給動(dòng)畫控制器。
 
復(fù)制代碼 代碼如下:
     
// MainViewController 
@interface MTHMainViewController () <UINavigationControllerDelegate,UIViewControllerTransitioningDelegate> 
 
@property (nonatomic,strong) MTHCustomAnimator *customAnimator; 
@property (nonatomic,strong) PDTransitionAnimator *minToMaxAnimator; 
@property (nonatomic,strong) MTHNextViewController *nextVC; 
// 交互控制器 (Interaction Controllers) 通過遵從 UIViewControllerInteractiveTransitioning 協(xié)議來控制可交互式的轉(zhuǎn)場(chǎng)。 
@property (strong, nonatomic) UIPercentDrivenInteractiveTransition* interactionController; 
@end 
 
@implementation MTHMainViewController 
 
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil 

    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 
    if (self) { 
        // Custom initialization 
    } 
    return self; 

 
- (void)viewDidLoad 

    [super viewDidLoad]; 
    // Do any additional setup after loading the view. 
    self.navigationItem.title = @"Demo"; 
    self.view.backgroundColor = [UIColor yellowColor]; 
    // 設(shè)置代理 
    self.navigationController.delegate = self; 
    // 設(shè)置轉(zhuǎn)場(chǎng)動(dòng)畫 
    self.customAnimator = [[MTHCustomAnimator alloc] init]; 
    self.minToMaxAnimator = [PDTransitionAnimator new]; 
 
    self.nextVC = [[MTHNextViewController alloc] init]; 
    // Present的代理和自定義設(shè)置 
    _nextVC.transitioningDelegate = self; 
    _nextVC.modalPresentationStyle = UIModalPresentationCustom; (貌似有BUG)換成modalTransitionStyle = UIModalPresentationCustom 
     
    // Push 
    UIButton *pushButton = [UIButton buttonWithType:UIButtonTypeSystem]; 
    pushButton.frame = CGRectMake(140, 200, 40, 40); 
    [pushButton setTitle:@"Push" forState:UIControlStateNormal]; 
    [pushButton addTarget:self action:@selector(push) forControlEvents:UIControlEventTouchUpInside]; 
    [self.view addSubview:pushButton]; 
     
    // Present 
    UIButton *modalButton = [UIButton buttonWithType:UIButtonTypeSystem]; 
    modalButton.frame = CGRectMake(265, 500, 50, 50); 
    [modalButton setTitle:@"Modal" forState:UIControlStateNormal]; 
    [modalButton addTarget:self action:@selector(modal) forControlEvents:UIControlEventTouchUpInside]; 
    [self.view addSubview:modalButton]; 
     
    // 實(shí)現(xiàn)交互操作的手勢(shì) 
    UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(didClickPanGestureRecognizer:)]; 
    [self.navigationController.view addGestureRecognizer:panRecognizer]; 

 
 
- (void)push 

    [self.navigationController pushViewController:_nextVC animated:YES]; 

 
- (void)modal 

    [self presentViewController:_nextVC animated:YES completion:nil]; 

 
#pragma mark - UINavigationControllerDelegate iOS7新增的2個(gè)方法 
// 動(dòng)畫特效 
- (id<UIViewControllerAnimatedTransitioning>) navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC 

    /**
     *  typedef NS_ENUM(NSInteger, UINavigationControllerOperation) {
     *     UINavigationControllerOperationNone,
     *     UINavigationControllerOperationPush,
     *     UINavigationControllerOperationPop,
     *  };
     */ 
    if (operation == UINavigationControllerOperationPush) { 
        return self.customAnimator; 
    }else{ 
        return nil; 
    } 

 
// 交互 
- (id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController*)navigationController                           interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>)animationController 

    /**
     *  在非交互式動(dòng)畫效果中,該方法返回 nil
     *  交互式轉(zhuǎn)場(chǎng),自我理解意思是,用戶能通過自己的動(dòng)作來(常見:手勢(shì))控制,不同于系統(tǒng)缺省給定的push或者pop(非交互式)
     */ 
    return _interactionController; 
}  
 
#pragma mark - Transitioning Delegate (Modal) 
// 前2個(gè)用于動(dòng)畫 
-(id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source 

    self.minToMaxAnimator.animationType = AnimationTypePresent; 
    return _minToMaxAnimator; 

 
-(id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed 

    self.minToMaxAnimator.animationType = AnimationTypeDismiss; 
    return _minToMaxAnimator; 

 
// 后2個(gè)用于交互 
- (id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator 

    return _interactionController; 

 
- (id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator 

    return nil; 

以上實(shí)現(xiàn)的是非交互的轉(zhuǎn)場(chǎng),指的是完全按照系統(tǒng)指定的切換機(jī)制,用戶無法中途取消或者控制進(jìn)度切換.那怎么來實(shí)現(xiàn)交互轉(zhuǎn)場(chǎng)呢:
UIPercentDrivenInteractiveTransition實(shí)現(xiàn)了UIViewControllerInteractiveTransitioning接口的類,,可以用一個(gè)百分比來控制交互式切換的過程。我們?cè)谑謩?shì)識(shí)別中只需要告訴這個(gè)類的實(shí)例當(dāng)前的狀態(tài)百分比如何,系統(tǒng)便根據(jù)這個(gè)百分比和我們之前設(shè)定的遷移方式為我們計(jì)算當(dāng)前應(yīng)該的UI渲染,十分方便。具體的幾個(gè)重要方法:
-(void)updateInteractiveTransition:(CGFloat)percentComplete 更新百分比,一般通過手勢(shì)識(shí)別的長(zhǎng)度之類的來計(jì)算一個(gè)值,然后進(jìn)行更新。之后的例子里會(huì)看到詳細(xì)的用法
-(void)cancelInteractiveTransition 報(bào)告交互取消,返回切換前的狀態(tài)
–(void)finishInteractiveTransition 報(bào)告交互完成,更新到切換后的狀態(tài)

復(fù)制代碼 代碼如下:
#pragma mark - 手勢(shì)交互的主要實(shí)現(xiàn)--->UIPercentDrivenInteractiveTransition 
- (void)didClickPanGestureRecognizer:(UIPanGestureRecognizer*)recognizer 

    UIView* view = self.view; 
    if (recognizer.state == UIGestureRecognizerStateBegan) { 
        // 獲取手勢(shì)的觸摸點(diǎn)坐標(biāo) 
        CGPoint location = [recognizer locationInView:view]; 
        // 判斷,用戶從右半邊滑動(dòng)的時(shí)候,推出下一個(gè)VC(根據(jù)實(shí)際需要是推進(jìn)還是推出) 
        if (location.x > CGRectGetMidX(view.bounds) && self.navigationController.viewControllers.count == 1){ 
            self.interactionController = [[UIPercentDrivenInteractiveTransition alloc] init]; 
            // 
            [self presentViewController:_nextVC animated:YES completion:nil]; 
        } 
    } else if (recognizer.state == UIGestureRecognizerStateChanged) { 
        // 獲取手勢(shì)在視圖上偏移的坐標(biāo) 
        CGPoint translation = [recognizer translationInView:view]; 
        // 根據(jù)手指拖動(dòng)的距離計(jì)算一個(gè)百分比,切換的動(dòng)畫效果也隨著這個(gè)百分比來走 
        CGFloat distance = fabs(translation.x / CGRectGetWidth(view.bounds)); 
        // 交互控制器控制動(dòng)畫的進(jìn)度 
        [self.interactionController updateInteractiveTransition:distance]; 
    } else if (recognizer.state == UIGestureRecognizerStateEnded) { 
        CGPoint translation = [recognizer translationInView:view]; 
        // 根據(jù)手指拖動(dòng)的距離計(jì)算一個(gè)百分比,切換的動(dòng)畫效果也隨著這個(gè)百分比來走 
        CGFloat distance = fabs(translation.x / CGRectGetWidth(view.bounds)); 
        // 移動(dòng)超過一半就強(qiáng)制完成 
        if (distance > 0.5) { 
            [self.interactionController finishInteractiveTransition]; 
        } else { 
            [self.interactionController cancelInteractiveTransition]; 
        } 
        // 結(jié)束后一定要置為nil 
        self.interactionController = nil; 
    } 

最后,給大家分享一個(gè)動(dòng)畫特效:類似于飛兔云傳的發(fā)送ViewController切換

復(fù)制代碼 代碼如下:
@implementation PDTransitionAnimator 
 
#define Switch_Time 1.2 
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext { 
    return Switch_Time; 

 
#define Button_Width 50.f 
#define Button_Space 10.f 
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext { 
    UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; 
    UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; 
 
    UIView * toView = toViewController.view; 
    UIView * fromView = fromViewController.view; 
     
    if (self.animationType == AnimationTypeDismiss) { 
        // 這個(gè)方法能夠高效的將當(dāng)前顯示的view截取成一個(gè)新的view.你可以用這個(gè)截取的view用來顯示.例如,也許你只想用一張截圖來做動(dòng)畫,畢竟用原始的view做動(dòng)畫代價(jià)太高.因?yàn)槭墙厝×艘呀?jīng)存在的內(nèi)容,這個(gè)方法只能反應(yīng)出這個(gè)被截取的view當(dāng)前的狀態(tài)信息,而不能反應(yīng)這個(gè)被截取的view以后要顯示的信息.然而,不管怎么樣,調(diào)用這個(gè)方法都會(huì)比將view做成截圖來加載效率更高. 
        UIView * snap = [toView snapshotViewAfterScreenUpdates:YES]; 
        [transitionContext.containerView addSubview:snap]; 
        [snap setFrame:CGRectMake([UIScreen mainScreen].bounds.size.width - Button_Width - Button_Space, [UIScreen mainScreen].bounds.size.height - Button_Width - Button_Space, Button_Width, Button_Width)]; 
         
        [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{ 
            [snap setFrame:[UIScreen mainScreen].bounds]; 
        } completion:^(BOOL finished) { 
            [UIView animateWithDuration:0.5 animations:^{ 
                [[transitionContext containerView] addSubview:toView]; 
                snap.alpha = 0;  
            } completion:^(BOOL finished) { 
                [snap removeFromSuperview]; 
                [transitionContext completeTransition:![transitionContext transitionWasCancelled]]; 
            }]; 
        }]; 
    } else { 
        UIView * snap2 = [toView snapshotViewAfterScreenUpdates:YES]; 
        [transitionContext.containerView addSubview:snap2]; 
        UIView * snap = [fromView snapshotViewAfterScreenUpdates:YES]; 
        [transitionContext.containerView addSubview:snap]; 
         
        [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{ 
            [snap setFrame:CGRectMake([UIScreen mainScreen].bounds.size.width - Button_Width - Button_Space+ (Button_Width/2), [UIScreen mainScreen].bounds.size.height - Button_Width - Button_Space + (Button_Width/2), 0, 0)]; 
        } completion:^(BOOL finished) { 
            [UIView animateWithDuration:0.5 animations:^{ 
                //snap.alpha = 0; 
            } completion:^(BOOL finished) { 
                [snap removeFromSuperview]; 
                [snap2 removeFromSuperview]; 
                [[transitionContext containerView] addSubview:toView]; 
                // 切記不要忘記了噢 
                [transitionContext completeTransition:![transitionContext transitionWasCancelled]]; 
            }]; 
        }]; 
         
    } 

其中,snapshotViewAfterScreenUpdates 方法的解釋,我也不是很懂,反正初級(jí)來說會(huì)用就行,還可以參照下面的解析:
在iOS7 以前, 獲取一個(gè)UIView的快照有以下步驟: 首先創(chuàng)建一個(gè)UIGraphics的圖像上下文,然后將視圖的layer渲染到該上下文中,從而取得一個(gè)圖像,最后關(guān)閉圖像上下文,并將圖像顯示在UIImageView中?,F(xiàn)在我們只需要一行代碼就可以完成上述步驟了:
復(fù)制代碼 代碼如下:
[view snapshotViewAfterScreenUpdates:NO];

這個(gè)方法制作了一個(gè)UIView的副本,如果我們希望視圖在執(zhí)行動(dòng)畫之前保存現(xiàn)在的外觀,以備之后使用(動(dòng)畫中視圖可能會(huì)被子視圖遮蓋或者發(fā)生其他一些變化),該方法就特別方便。
afterUpdates參數(shù)表示是否在所有效果應(yīng)用在視圖上了以后再獲取快照。例如,如果該參數(shù)為NO,則立馬獲取該視圖現(xiàn)在狀態(tài)的快照,反之,以下代碼只能得到一個(gè)空白快照:
復(fù)制代碼 代碼如下:
[view snapshotViewAfterScreenUpdates:YES];
[view setAlpha:0.0];

由于我們?cè)O(shè)置afterUpdates參數(shù)為YES,而視圖的透明度值被設(shè)置成了0,所以方法將在該設(shè)置應(yīng)用在視圖上了之后才進(jìn)行快照,于是乎屏幕空空如也。另外就是……你可以對(duì)快照再進(jìn)行快照……繼續(xù)快照……

繼續(xù)前面的內(nèi)容,這一章,主要介紹自定義ViewController容器上視圖VC的切換.先來看看系統(tǒng)給我們提供的容器控制器 UINavigationController和UITabBarController 都有一個(gè)NSArray類型的屬性viewControllers,很明顯,存儲(chǔ)的就是需要切換的視圖VC.同理,我們定義一個(gè)ContainerViewController,是UIViewController的直接子類,用來作為容器依托,額,其他屬性定義詳見代碼吧,這里不多說了.(PS:原先我進(jìn)行多個(gè)自定義視圖VC切換的方法,是放置一個(gè)UIScrollView,然后把所有childViewController的View的frame的X坐標(biāo),依此按320遞增,大家可以自行想象下,這樣不好的地方,我感覺就是所有的VC一經(jīng)加載就全部實(shí)體化了,而且不會(huì)因?yàn)楸磺袚Q變成暫不顯示而釋放掉)
偷懶下,用storyboard創(chuàng)建的5個(gè)childVC

復(fù)制代碼 代碼如下:

// ContainerViewController 
@interface FTContainerViewController ()  
 
@property (strong, nonatomic) FTPhotoSenderViewController       *photoSenderViewController; 
@property (strong, nonatomic) FTVideoSenderViewController       *videoSenderViewController; 
@property (strong, nonatomic) FTFileSenderViewController        *fileSenderViewController; 
@property (strong, nonatomic) FTContactSenderViewController     *contactSenderViewController; 
@property (strong, nonatomic) FTClipboardSenderViewController   *clipboardSenderViewController; 
@property (strong, nonatomic) UIViewController                  *selectedViewController;        // 當(dāng)前選擇的VC 
@property (strong, nonatomic) NSArray                           *viewControllers;               // childVC數(shù)組 
@property (assign, nonatomic) NSInteger                         currentControllerIndex;         // 當(dāng)前選擇的VC的數(shù)組下標(biāo)號(hào) 
 
 
@end 
 
@implementation FTContainerViewController 
 
#pragma mark - ViewLifecycle Methods 
 
- (void)viewDidLoad 

    [super viewDidLoad]; 
    // childVC 
    self.photoSenderViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"FTPhotoSenderViewController"]; 
    self.videoSenderViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"FTVideoSenderViewController"]; 
    self.fileSenderViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"FTFileSenderViewController"]; 
    self.contactSenderViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"FTContactSenderViewController"]; 
    self.clipboardSenderViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"FTClipboardSenderViewController"]; 
    // 存儲(chǔ)childVC的數(shù)組 
    self.viewControllers = @[_photoSenderViewController,_videoSenderViewController,_fileSenderViewController,_contactSenderViewController,_clipboardSenderViewController]; 
    // 缺省為下標(biāo)為0的VC 
    self.selectedViewController = self.selectedViewController ?: self.viewControllers[0]; 
    self.currentControllerIndex = 0; 


依舊,實(shí)現(xiàn)UIViewControllerAnimatedTransitioning協(xié)議的Animator類,不過里面換個(gè)動(dòng)畫效果,利用iOS7新增的彈簧動(dòng)畫效果:
復(fù)制代碼 代碼如下:

#import "FTMthTransitionAnimator.h" 
 
@implementation FTMthTransitionAnimator 
 
static CGFloat const kChildViewPadding = 16; 
static CGFloat const kDamping = 0.5;    // damping參數(shù)代表彈性阻尼,隨著阻尼值越來越接近0.0,動(dòng)畫的彈性效果會(huì)越來越明顯,而如果設(shè)置阻尼值為1.0,則視圖動(dòng)畫不會(huì)有彈性效果 
static CGFloat const kInitialSpringVelocity = 0.5;  // 初始化彈簧速率 
 
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext 

    return 1.0; 

 
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext 

    /**
     *  - viewControllerForKey:我們可以通過他訪問過渡的兩個(gè) ViewController。
     *  - containerView:兩個(gè) ViewController 的 containerView。
     *  - initialFrameForViewController 和 finalFrameForViewController 是過渡開始和結(jié)束時(shí)每個(gè) ViewController 的 frame。
     */ 
    UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; 
    UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; 
     
    [[transitionContext containerView] addSubview:toViewController.view]; 
    toViewController.view.alpha = 0; 
    BOOL goingRight = ([transitionContext initialFrameForViewController:toViewController].origin.x < [transitionContext finalFrameForViewController:toViewController].origin.x); 
    CGFloat transDistance = [transitionContext containerView].bounds.size.width + kChildViewPadding; 
    CGAffineTransform transform = CGAffineTransformMakeTranslation(goingRight ? transDistance : -transDistance, 0); 
    // CGAffineTransformInvert 反轉(zhuǎn) 
    toViewController.view.transform = CGAffineTransformInvert(transform); 
//    toViewController.view.transform = CGAffineTransformTranslate(toViewController.view.transform, (goingRight ? transDistance : -transDistance), 0); 
     
    /**
     *   ----------彈簧動(dòng)畫.....-------
     *  使用由彈簧的運(yùn)動(dòng)描述的時(shí)序曲線` animations` 。當(dāng)` dampingRatio`為1時(shí),動(dòng)畫將平穩(wěn)減速到其最終的模型值不會(huì)振蕩。阻尼比小于1來完全停止前將振蕩越來越多。可以使用彈簧的初始速度,以指定的速度在模擬彈簧的端部的物體被移動(dòng)它附著之前。這是一個(gè)單元坐標(biāo)系,其中1是指行駛總距離的動(dòng)畫在第二。所以,如果你改變一個(gè)物體的位置由200PT在這個(gè)動(dòng)畫,以及你想要的動(dòng)畫表現(xiàn)得好像物體在動(dòng),在100PT /秒的動(dòng)畫開始之前,你會(huì)通過0.5 。你通常會(huì)想通過0的速度。
     */ 
    [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0 usingSpringWithDamping:kDamping initialSpringVelocity:kInitialSpringVelocity options:0x00 animations:^{ 
        fromViewController.view.transform = transform; 
        fromViewController.view.alpha = 0; 
        // CGAffineTransformIdentity  重置,初始化 
        toViewController.view.transform = CGAffineTransformIdentity; 
        toViewController.view.alpha = 1; 
    } completion:^(BOOL finished) { 
        fromViewController.view.transform = CGAffineTransformIdentity; 
        // 聲明過渡結(jié)束-->記住,一定別忘了在過渡結(jié)束時(shí)調(diào)用 completeTransition: 這個(gè)方法。 
        [transitionContext completeTransition:![transitionContext transitionWasCancelled]]; 
    }]; 

 
@end 

接下來的代碼,就是實(shí)現(xiàn)自定義容器切換的關(guān)鍵了.通常情況下,當(dāng)我們使用系統(tǒng)內(nèi)建的類時(shí),系統(tǒng)框架為我們創(chuàng)建了轉(zhuǎn)場(chǎng)上下文對(duì)象,并把它傳遞給動(dòng)畫控制器。但是在我們這種情況下,我們需要自定義轉(zhuǎn)場(chǎng)動(dòng)畫,所以我們需要承擔(dān)系統(tǒng)框架的責(zé)任,自己去創(chuàng)建這個(gè)轉(zhuǎn)場(chǎng)上下文對(duì)象。

復(fù)制代碼 代碼如下:

@interface FTMthTransitionContext : NSObject <UIViewControllerContextTransitioning> 
 
- (instancetype)initWithFromViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController goingRight:(BOOL)goingRight; 
@property (nonatomic, copy) void (^completionBlock)(BOOL didComplete); 
@property (nonatomic, assign, getter=isAnimated) BOOL animated; 
@property (nonatomic, assign, getter=isInteractive) BOOL interactive; // 是否交互式 
 
@property (nonatomic, strong) NSDictionary *privateViewControllers; 
@property (nonatomic, assign) CGRect privateDisappearingFromRect; 
@property (nonatomic, assign) CGRect privateAppearingFromRect; 
@property (nonatomic, assign) CGRect privateDisappearingToRect; 
@property (nonatomic, assign) CGRect privateAppearingToRect; 
@property (nonatomic, weak) UIView *containerView; 
@property (nonatomic, assign) UIModalPresentationStyle presentationStyle; 
 
@end 
 
@implementation FTMthTransitionContext 
 
- (instancetype)initWithFromViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController goingRight:(BOOL)goingRight { 
     
    if ((self = [super init])) { 
        self.presentationStyle = UIModalPresentationCustom; 
        self.containerView = fromViewController.view.superview; 
        self.privateViewControllers = @{ 
                                        UITransitionContextFromViewControllerKey:fromViewController, 
                                        UITransitionContextToViewControllerKey:toViewController, 
                                        }; 
         
        // Set the view frame properties which make sense in our specialized ContainerViewController context. Views appear from and disappear to the sides, corresponding to where the icon buttons are positioned. So tapping a button to the right of the currently selected, makes the view disappear to the left and the new view appear from the right. The animator object can choose to use this to determine whether the transition should be going left to right, or right to left, for example. 
        CGFloat travelDistance = (goingRight ? -self.containerView.bounds.size.width : self.containerView.bounds.size.width); 
        self.privateDisappearingFromRect = self.privateAppearingToRect = self.containerView.bounds; 
        self.privateDisappearingToRect = CGRectOffset (self.containerView.bounds, travelDistance, 0); 
        self.privateAppearingFromRect = CGRectOffset (self.containerView.bounds, -travelDistance, 0); 
    } 
     
    return self; 

 
- (CGRect)initialFrameForViewController:(UIViewController *)viewController { 
    if (viewController == [self viewControllerForKey:UITransitionContextFromViewControllerKey]) { 
        return self.privateDisappearingFromRect; 
    } else { 
        return self.privateAppearingFromRect; 
    } 

 
- (CGRect)finalFrameForViewController:(UIViewController *)viewController { 
    if (viewController == [self viewControllerForKey:UITransitionContextFromViewControllerKey]) { 
        return self.privateDisappearingToRect; 
    } else { 
        return self.privateAppearingToRect; 
    } 

 
- (UIViewController *)viewControllerForKey:(NSString *)key { 
    return self.privateViewControllers[key]; 

 
- (void)completeTransition:(BOOL)didComplete { 
    if (self.completionBlock) { 
        self.completionBlock (didComplete); 
    } 

 
// 非交互式,直接返回NO,因?yàn)椴辉试S交互當(dāng)然也就無法操作進(jìn)度取消 
- (BOOL)transitionWasCancelled { return NO; } 
 
// 非交互式,直接不進(jìn)行操作,只有進(jìn)行交互,下面3個(gè)協(xié)議方法才有意義,可參照系統(tǒng)給我們定義好的交互控制器 
// @interface UIPercentDrivenInteractiveTransition : NSObject <UIViewControllerInteractiveTransitioning> 
- (void)updateInteractiveTransition:(CGFloat)percentComplete {} 
- (void)finishInteractiveTransition {} 
- (void)cancelInteractiveTransition {} 
 
@end 

OK,準(zhǔn)備工作都做好了,為了仿照UIScrollView的滑動(dòng)切換,但又因?yàn)楝F(xiàn)在展示的是非交互式,我們定義一個(gè)swip(輕掃)手勢(shì).

復(fù)制代碼 代碼如下:

UISwipeGestureRecognizer *leftGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swapController:)]; 
[leftGesture setDirection:UISwipeGestureRecognizerDirectionLeft]; 
[self.view addGestureRecognizer:leftGesture]; 
 
UISwipeGestureRecognizer *rightGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swapController:)]; 
[rightGesture setDirection:UISwipeGestureRecognizerDirectionRight]; 
[self.view addGestureRecognizer:rightGesture]; 
[objc] view plaincopy
// 響應(yīng)手勢(shì)的方法 
- (void)swapViewControllers:(UISwipeGestureRecognizer *)swipeGestureRecognizer 

    if (swipeGestureRecognizer.direction == UISwipeGestureRecognizerDirectionLeft) { 
        if (_currentControllerIndex < 4) { 
            _currentControllerIndex++; 
        } 
        NSLog(@"_currentControllerIndex = %ld",(long)_currentControllerIndex); 
        UIViewController *selectedViewController = self.viewControllers[_currentControllerIndex]; 
         NSLog(@"%s__%d__|%@",__FUNCTION__,__LINE__,@"右邊"); 
         self.selectedViewController = selectedViewController; 
    } else if (swipeGestureRecognizer.direction == UISwipeGestureRecognizerDirectionRight){ 
         NSLog(@"%s__%d__|%@",__FUNCTION__,__LINE__,@"左邊"); 
        if (_currentControllerIndex > 0) { 
            _currentControllerIndex--; 
        } 
        UIViewController *selectedViewController = self.viewControllers[_currentControllerIndex]; 
        self.selectedViewController = selectedViewController; 
    } 

 
// 重寫selectedViewController的setter 
- (void)setSelectedViewController:(UIViewController *)selectedViewController 

    NSParameterAssert (selectedViewController); 
    [self _transitionToChildViewController:selectedViewController]; 
    _selectedViewController = selectedViewController; 

 
// 切換操作(自定義的,聯(lián)想我在前面文章網(wǎng)易標(biāo)簽欄切換中,系統(tǒng)給的transitionFromViewController,是一個(gè)道理) 
- (void)_transitionToChildViewController:(UIViewController *)toViewController 

    UIViewController *fromViewController = self.childViewControllers.count > 0 ? self.childViewControllers[0] : nil; 
    if (toViewController == fromViewController) { 
        return; 
    } 
    UIView *toView = toViewController.view; 
    [toView setTranslatesAutoresizingMaskIntoConstraints:YES]; 
    toView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 
    toView.frame = self.view.bounds; 
     
    // 自定義容器的切換,addChildViewController是關(guān)鍵,它保證了你想要顯示的VC能夠加載到容器中 
    // 而所謂的動(dòng)畫和上下文,只是為了轉(zhuǎn)場(chǎng)的動(dòng)畫效果 
    // 因此,就算用UIScrollView切換,也不能缺少addChildViewController,切記!切記! 
    [fromViewController willMoveToParentViewController:nil]; 
    [self addChildViewController:toViewController]; 
     
    if (!fromViewController) { 
        [self.view addSubview:toViewController.view]; 
        [toViewController didMoveToParentViewController:self]; 
        return; 
    } 
     
    // Animator 
    FTMthTransitionAnimator *transitionAnimator = [[FTMthTransitionAnimator alloc] init]; 
    NSUInteger fromIndex = [self.viewControllers indexOfObject:fromViewController]; 
    NSUInteger toIndex = [self.viewControllers indexOfObject:toViewController]; 
     
    // Context 
    FTMthTransitionContext *transitionContext = [[FTMthTransitionContext alloc] initWithFromViewController:fromViewController toViewController:toViewController goingRight:(toIndex > fromIndex)]; 
    transitionContext.animated = YES; 
    transitionContext.interactive = NO; 
    transitionContext.completionBlock = ^(BOOL didComplete) { 
        // 因?yàn)槭欠墙换ナ?所以fromVC可以直接直接remove出its parent's children controllers array 
        [fromViewController.view removeFromSuperview]; 
        [fromViewController removeFromParentViewController]; 
        [toViewController didMoveToParentViewController:self]; 
        if ([transitionAnimator respondsToSelector:@selector (animationEnded:)]) { 
            [transitionAnimator animationEnded:didComplete]; 
        } 
    }; 
    // 轉(zhuǎn)場(chǎng)動(dòng)畫需要以轉(zhuǎn)場(chǎng)上下文為依托,因?yàn)槲覀兪亲远x的Context,所以要手動(dòng)設(shè)置 
    [transitionAnimator animateTransition:transitionContext]; 
}

大功告成.
上面展示的就是一個(gè)基本的自定義容器的非交互式的轉(zhuǎn)場(chǎng)切換.那交互式的呢?從上面我定義手勢(shì)定義為swip而不是pan也可以看出,非交互轉(zhuǎn)場(chǎng),并不能完全實(shí)現(xiàn)UIScrollView那種分頁式的效果,按照類似百分比的形式來進(jìn)行fromVC和toVC的切換,因?yàn)槲覀內(nèi)鄙俳换タ刂破?在自定義的容器中,系統(tǒng)是沒有提供返回交互控制器的協(xié)議給我們的,查了蠻多資料,也沒找到給出明確的方法,我認(rèn)為,要跟實(shí)現(xiàn)轉(zhuǎn)場(chǎng)上下文一樣,仿照系統(tǒng)方法,自定義的去實(shí)現(xiàn)交互式的協(xié)議方法.我們就要去思考,系統(tǒng)是如何搭建起這個(gè)環(huán)境的.

相關(guān)文章

  • iOS開發(fā)之自定義UITextField的方法

    iOS開發(fā)之自定義UITextField的方法

    UITextField是IOS開發(fā)中用戶交互中重要的一個(gè)控件,常被用來做賬號(hào)密碼框,輸入信息框等。本文給大家介紹iOS開發(fā)之自定義UITextField的方法,感興趣的朋友一起學(xué)習(xí)吧
    2016-05-05
  • IOS 中彈框的實(shí)現(xiàn)方法整理

    IOS 中彈框的實(shí)現(xiàn)方法整理

    這篇文章主要介紹了IOS 中彈框的實(shí)現(xiàn)方法整理的相關(guān)資料,希望通過本文能幫助到大家,需要的朋友可以參考下
    2017-09-09
  • IOS開發(fā)仿微信右側(cè)彈出視圖實(shí)現(xiàn)

    IOS開發(fā)仿微信右側(cè)彈出視圖實(shí)現(xiàn)

    這篇文章主要介紹了IOS開發(fā)仿微信右側(cè)彈出視圖實(shí)現(xiàn)的相關(guān)資料,希望通過本文能幫助到大家,讓大家實(shí)現(xiàn)這樣類似的功能,需要的朋友可以參考下
    2017-10-10
  • iOS實(shí)現(xiàn)逐幀動(dòng)畫做loading視圖

    iOS實(shí)現(xiàn)逐幀動(dòng)畫做loading視圖

    這篇文章主要為大家詳細(xì)介紹了iOS實(shí)現(xiàn)逐幀動(dòng)畫做loading視圖,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-05-05
  • iOS(閉包)block傳值詳解

    iOS(閉包)block傳值詳解

    這篇文章主要介紹了iOS(閉包)block傳值的相關(guān)知識(shí)以及代碼分享,對(duì)此有興趣的朋友可以學(xué)習(xí)下。
    2018-02-02
  • iOS 對(duì)NSMutableArray進(jìn)行排序和過濾的實(shí)例

    iOS 對(duì)NSMutableArray進(jìn)行排序和過濾的實(shí)例

    下面小編就為大家分享一篇iOS 對(duì)NSMutableArray進(jìn)行排序和過濾的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-01-01
  • iOS中打包上傳常見的錯(cuò)誤與解決辦法

    iOS中打包上傳常見的錯(cuò)誤與解決辦法

    關(guān)于打包上傳至AppStore,大家都認(rèn)為是最后一步了,其實(shí)到了這里往往會(huì)遇到很多的坑。對(duì)于踩過的坑我不想再踩第二遍,所以在此將我遇到的所有奇葩問題在此做一個(gè)記錄,當(dāng)作對(duì)自己的一個(gè)提醒,同時(shí)也分享給給位,需要的朋友可以參考下。
    2017-03-03
  • iOS 模塊化之JLRoute路由示例

    iOS 模塊化之JLRoute路由示例

    這篇文章主要介紹了iOS 模塊化之JLRoute路由示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-02-02
  • Objective-C中NSNumber與NSDictionary的用法簡(jiǎn)介

    Objective-C中NSNumber與NSDictionary的用法簡(jiǎn)介

    這篇文章主要介紹了Objective-C中NSNumber與NSDictionary的用法簡(jiǎn)介,雖然Objective-C即將不再是iOS的主流開發(fā)語言...well,需要的朋友可以參考下
    2015-09-09
  • iOS實(shí)現(xiàn)不規(guī)則Button點(diǎn)擊效果實(shí)例代碼

    iOS實(shí)現(xiàn)不規(guī)則Button點(diǎn)擊效果實(shí)例代碼

    這篇文章主要給大家介紹了關(guān)于iOS實(shí)現(xiàn)不規(guī)則Button點(diǎn)擊的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)各位iOS開發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04

最新評(píng)論