亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

iOS開發(fā)中的ViewController轉場切換效果實現(xiàn)簡介

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

在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)易標簽欄的效果中已經(jīng)做了闡述,但是它提供的容器轉場動畫只可以實現(xiàn)一些簡單的UIView動畫,但是難以重用,耦合高.
(2)關鍵的API:
A.動畫控制器 (Animation Controllers) 遵從 UIViewControllerAnimatedTransitioning 協(xié)議,并且負責實際執(zhí)行動畫。
B.交互控制器 (Interaction Controllers) 通過遵從 UIViewControllerInteractiveTransitioning 協(xié)議來控制可交互式的轉場。
C.轉場代理 (Transitioning Delegates) 根據(jù)不同的轉場類型方便的提供需要的動畫控制器和交互控制器。
   其中UINavigationControllerDelegate delegate 中新增了2個方法給NavigationController
               UIViewControllerTransitioningDelegate 新增transitioningDelegate  給Modal的Present和Dismis

復制代碼 代碼如下:

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

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

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

    return 1.0; 

 
// 完成容器轉場動畫的主要方法,我們對于切換時的UIView的設置和動畫都在這個方法中完成 
- (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:^{ 
        // 動畫效果有很多,這里就展示個左偏移 
        fromViewController.view.transform = CGAffineTransformMakeTranslation(-320, 0); 
        toViewController.view.alpha = 1.0; 
    } completion:^(BOOL finished) { 
        fromViewController.view.transform = CGAffineTransformIdentity; 
        // 聲明過渡結束-->記住,一定別忘了在過渡結束時調(diào)用 completeTransition: 這個方法 
        [transitionContext completeTransition:![transitionContext transitionWasCancelled]]; 
    }]; 

       PS:從協(xié)議中兩個方法可以看出,上面兩個必須實現(xiàn)的方法需要一個轉場上下文參數(shù),這是一個遵從UIViewControllerContextTransitioning 協(xié)議的對象。通常情況下,當我們使用系統(tǒng)的類時,系統(tǒng)框架為我們提供的轉場代理(Transitioning Delegates),為我們創(chuàng)建了轉場上下文對象,并把它傳遞給動畫控制器。
 
復制代碼 代碼如下:
     
// MainViewController 
@interface MTHMainViewController () <UINavigationControllerDelegate,UIViewControllerTransitioningDelegate> 
 
@property (nonatomic,strong) MTHCustomAnimator *customAnimator; 
@property (nonatomic,strong) PDTransitionAnimator *minToMaxAnimator; 
@property (nonatomic,strong) MTHNextViewController *nextVC; 
// 交互控制器 (Interaction Controllers) 通過遵從 UIViewControllerInteractiveTransitioning 協(xié)議來控制可交互式的轉場。 
@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]; 
    // 設置代理 
    self.navigationController.delegate = self; 
    // 設置轉場動畫 
    self.customAnimator = [[MTHCustomAnimator alloc] init]; 
    self.minToMaxAnimator = [PDTransitionAnimator new]; 
 
    self.nextVC = [[MTHNextViewController alloc] init]; 
    // Present的代理和自定義設置 
    _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]; 
     
    // 實現(xiàn)交互操作的手勢 
    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個方法 
// 動畫特效 
- (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 

    /**
     *  在非交互式動畫效果中,該方法返回 nil
     *  交互式轉場,自我理解意思是,用戶能通過自己的動作來(常見:手勢)控制,不同于系統(tǒng)缺省給定的push或者pop(非交互式)
     */ 
    return _interactionController; 
}  
 
#pragma mark - Transitioning Delegate (Modal) 
// 前2個用于動畫 
-(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個用于交互 
- (id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator 

    return _interactionController; 

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

    return nil; 

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

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

    UIView* view = self.view; 
    if (recognizer.state == UIGestureRecognizerStateBegan) { 
        // 獲取手勢的觸摸點坐標 
        CGPoint location = [recognizer locationInView:view]; 
        // 判斷,用戶從右半邊滑動的時候,推出下一個VC(根據(jù)實際需要是推進還是推出) 
        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) { 
        // 獲取手勢在視圖上偏移的坐標 
        CGPoint translation = [recognizer translationInView:view]; 
        // 根據(jù)手指拖動的距離計算一個百分比,切換的動畫效果也隨著這個百分比來走 
        CGFloat distance = fabs(translation.x / CGRectGetWidth(view.bounds)); 
        // 交互控制器控制動畫的進度 
        [self.interactionController updateInteractiveTransition:distance]; 
    } else if (recognizer.state == UIGestureRecognizerStateEnded) { 
        CGPoint translation = [recognizer translationInView:view]; 
        // 根據(jù)手指拖動的距離計算一個百分比,切換的動畫效果也隨著這個百分比來走 
        CGFloat distance = fabs(translation.x / CGRectGetWidth(view.bounds)); 
        // 移動超過一半就強制完成 
        if (distance > 0.5) { 
            [self.interactionController finishInteractiveTransition]; 
        } else { 
            [self.interactionController cancelInteractiveTransition]; 
        } 
        // 結束后一定要置為nil 
        self.interactionController = nil; 
    } 

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

復制代碼 代碼如下:
@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) { 
        // 這個方法能夠高效的將當前顯示的view截取成一個新的view.你可以用這個截取的view用來顯示.例如,也許你只想用一張截圖來做動畫,畢竟用原始的view做動畫代價太高.因為是截取了已經(jīng)存在的內(nèi)容,這個方法只能反應出這個被截取的view當前的狀態(tài)信息,而不能反應這個被截取的view以后要顯示的信息.然而,不管怎么樣,調(diào)用這個方法都會比將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 方法的解釋,我也不是很懂,反正初級來說會用就行,還可以參照下面的解析:
在iOS7 以前, 獲取一個UIView的快照有以下步驟: 首先創(chuàng)建一個UIGraphics的圖像上下文,然后將視圖的layer渲染到該上下文中,從而取得一個圖像,最后關閉圖像上下文,并將圖像顯示在UIImageView中?,F(xiàn)在我們只需要一行代碼就可以完成上述步驟了:
復制代碼 代碼如下:
[view snapshotViewAfterScreenUpdates:NO];

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

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

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

復制代碼 代碼如下:

// 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;        // 當前選擇的VC 
@property (strong, nonatomic) NSArray                           *viewControllers;               // childVC數(shù)組 
@property (assign, nonatomic) NSInteger                         currentControllerIndex;         // 當前選擇的VC的數(shù)組下標號 
 
 
@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"]; 
    // 存儲childVC的數(shù)組 
    self.viewControllers = @[_photoSenderViewController,_videoSenderViewController,_fileSenderViewController,_contactSenderViewController,_clipboardSenderViewController]; 
    // 缺省為下標為0的VC 
    self.selectedViewController = self.selectedViewController ?: self.viewControllers[0]; 
    self.currentControllerIndex = 0; 


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

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

    return 1.0; 

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

    /**
     *  - viewControllerForKey:我們可以通過他訪問過渡的兩個 ViewController。
     *  - containerView:兩個 ViewController 的 containerView。
     *  - initialFrameForViewController 和 finalFrameForViewController 是過渡開始和結束時每個 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 反轉 
    toViewController.view.transform = CGAffineTransformInvert(transform); 
//    toViewController.view.transform = CGAffineTransformTranslate(toViewController.view.transform, (goingRight ? transDistance : -transDistance), 0); 
     
    /**
     *   ----------彈簧動畫.....-------
     *  使用由彈簧的運動描述的時序曲線` animations` 。當` dampingRatio`為1時,動畫將平穩(wěn)減速到其最終的模型值不會振蕩。阻尼比小于1來完全停止前將振蕩越來越多??梢允褂脧椈傻某跏妓俣龋灾付ǖ乃俣仍谀M彈簧的端部的物體被移動它附著之前。這是一個單元坐標系,其中1是指行駛總距離的動畫在第二。所以,如果你改變一個物體的位置由200PT在這個動畫,以及你想要的動畫表現(xiàn)得好像物體在動,在100PT /秒的動畫開始之前,你會通過0.5 。你通常會想通過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; 
        // 聲明過渡結束-->記住,一定別忘了在過渡結束時調(diào)用 completeTransition: 這個方法。 
        [transitionContext completeTransition:![transitionContext transitionWasCancelled]]; 
    }]; 

 
@end 

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

復制代碼 代碼如下:

@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,因為不允許交互當然也就無法操作進度取消 
- (BOOL)transitionWasCancelled { return NO; } 
 
// 非交互式,直接不進行操作,只有進行交互,下面3個協(xié)議方法才有意義,可參照系統(tǒng)給我們定義好的交互控制器 
// @interface UIPercentDrivenInteractiveTransition : NSObject <UIViewControllerInteractiveTransitioning> 
- (void)updateInteractiveTransition:(CGFloat)percentComplete {} 
- (void)finishInteractiveTransition {} 
- (void)cancelInteractiveTransition {} 
 
@end 

OK,準備工作都做好了,為了仿照UIScrollView的滑動切換,但又因為現(xiàn)在展示的是非交互式,我們定義一個swip(輕掃)手勢.

復制代碼 代碼如下:

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
// 響應手勢的方法 
- (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)易標簽欄切換中,系統(tǒng)給的transitionFromViewController,是一個道理) 
- (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是關鍵,它保證了你想要顯示的VC能夠加載到容器中 
    // 而所謂的動畫和上下文,只是為了轉場的動畫效果 
    // 因此,就算用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) { 
        // 因為是非交互式,所以fromVC可以直接直接remove出its parent's children controllers array 
        [fromViewController.view removeFromSuperview]; 
        [fromViewController removeFromParentViewController]; 
        [toViewController didMoveToParentViewController:self]; 
        if ([transitionAnimator respondsToSelector:@selector (animationEnded:)]) { 
            [transitionAnimator animationEnded:didComplete]; 
        } 
    }; 
    // 轉場動畫需要以轉場上下文為依托,因為我們是自定義的Context,所以要手動設置 
    [transitionAnimator animateTransition:transitionContext]; 
}

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

相關文章

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

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

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

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

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

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

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

    iOS實現(xiàn)逐幀動畫做loading視圖

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

    iOS(閉包)block傳值詳解

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

    iOS 對NSMutableArray進行排序和過濾的實例

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

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

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

    iOS 模塊化之JLRoute路由示例

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

    Objective-C中NSNumber與NSDictionary的用法簡介

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

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

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

最新評論