在這里我們使用一個(gè)例子來(lái)說(shuō)明動(dòng)畫的實(shí)現(xiàn)過(guò)程殴泰。這個(gè)例子為點(diǎn)擊對(duì)應(yīng)的圖片會(huì)展示該圖片對(duì)應(yīng)的大圖馅闽。使用系統(tǒng)的跳轉(zhuǎn)效果是這樣展示的歌亲。
接下來(lái)我們便要自定義動(dòng)畫來(lái)展示圖片了鄙信。
UIKit可以讓我們通過(guò)實(shí)現(xiàn)代理協(xié)議來(lái)定義自己的展示方式骆捧,我們只需要實(shí)現(xiàn)UIViewControllerTransitioningDelegate
就可以了狐榔。每次我們要present一個(gè)新的視圖控制器時(shí)暂筝,UIKit都會(huì)通過(guò)代理協(xié)議來(lái)詢問(wèn)我們是否要使用自定義的過(guò)渡方式拂共,
UIKit會(huì)調(diào)用方法animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
,如果這個(gè)方法返回的值是nil
料饥,則會(huì)使用系統(tǒng)默認(rèn)的過(guò)渡動(dòng)畫蒲犬,如果不是,UIKit則會(huì)使用自定義的動(dòng)畫對(duì)象來(lái)過(guò)渡岸啡。
而這里的對(duì)象是我們實(shí)現(xiàn)的類原叮,這個(gè)類實(shí)現(xiàn)了協(xié)議UIViewControllerAnimatedTransitioning
,這里會(huì)調(diào)用方法transitionDuration:
來(lái)確定動(dòng)畫的執(zhí)行時(shí)間巡蘸,在調(diào)用方法animateTransition:
奋隶,我們所實(shí)現(xiàn)的動(dòng)畫方式就是定義在這里面。
接下來(lái)我們就是看PresentationAnimation類的具體實(shí)現(xiàn)了悦荒。
先來(lái)了解一下animateTransition:
的唯一一個(gè)參數(shù)唯欣,這個(gè)參數(shù)是UIViewControllerContextTransitioning
類型的,通過(guò)它們可以得到過(guò)渡動(dòng)畫中很多數(shù)據(jù)
//動(dòng)畫過(guò)渡效果將在這個(gè)View上進(jìn)行
UIView *containerView = [transitionContext containerView];
//獲取將要被替換的視圖
UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
//獲取將要展現(xiàn)的視圖
UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
//獲取將要被替換的視圖控制器
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
//獲取將要展現(xiàn)的視圖控制器
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
再來(lái)了解一下這個(gè)過(guò)渡執(zhí)行的過(guò)程搬味,當(dāng)兩個(gè)視圖控制器之間的過(guò)渡開始時(shí)境氢,當(dāng)前已經(jīng)存在的視圖將會(huì)被添加到containerView上,而新的視圖控制器的視圖此時(shí)也被創(chuàng)建碰纬,只是不可見而已萍聊。
上面的這一步是自動(dòng)執(zhí)行的,接下來(lái)的就是我們的任務(wù)了嘀趟,將新的視圖添加到containerView上脐区,至于使用什么樣的方式使新視圖出現(xiàn)和使舊視圖消失愈诚,就看我們想要什么效果了她按。
默認(rèn)情況下,過(guò)渡動(dòng)畫執(zhí)行完之后炕柔,舊視圖會(huì)在containerView上移除酌泰,我們會(huì)在下面的小例子中進(jìn)行驗(yàn)證。
用個(gè)簡(jiǎn)單的小例子來(lái)說(shuō)明用法匕累,淡入淡出的方式
實(shí)現(xiàn)方法animateTransition:
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
//動(dòng)畫過(guò)渡效果將在這個(gè)View上進(jìn)行
UIView *containerView = [transitionContext containerView];
//獲取將要被替換的視圖
UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
//獲取將要展現(xiàn)的視圖
UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
[containerView addSubview:toView];
toView.alpha = 0.0f;
//取動(dòng)畫的時(shí)間陵刹,作為我們自定義的動(dòng)畫的時(shí)間
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
toView.alpha = 1.0f;
} completion:^(BOOL finished) {
//這樣的是為了如中途取消可設(shè)置為未完成,這樣可恢復(fù)原狀
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
實(shí)現(xiàn)效果:
我們?cè)诖a[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
后面加上這些代碼來(lái)驗(yàn)證fromView是否已經(jīng)移除
if ([[containerView subviews]containsObject:fromView]) {
NSLog(@"舊視圖未移除");
}else{
NSLog(@"舊視圖已移除");
}
會(huì)發(fā)現(xiàn)打印了 “舊視圖已移除”
當(dāng)然我們也可以實(shí)現(xiàn)稍微復(fù)雜一點(diǎn)的動(dòng)畫欢嘿,只需要重新方法animateTransition:
的實(shí)現(xiàn)就可以了
UIView *containerView = [transitionContext containerView];
UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
CGRect initialFrame = self.originFrame;
CGRect finalFrame = toView.frame;
CGFloat xScaleFactor = initialFrame.size.width/finalFrame.size.width;
CGFloat yScaleFactor = initialFrame.size.height/finalFrame.size.height;
CGAffineTransform scaleTransform = CGAffineTransformMakeScale(xScaleFactor, yScaleFactor);
toView.transform = scaleTransform;
toView.center = CGPointMake(CGRectGetMidX(initialFrame)*0.5, CGRectGetMidY(initialFrame)*0.5);
[containerView addSubview:toView];
[UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0 usingSpringWithDamping:0.4 initialSpringVelocity:0.0 options:0 animations:^{
toView.transform = CGAffineTransformIdentity;
toView.center = CGPointMake(CGRectGetMidX(finalFrame), CGRectGetMidY(finalFrame));
} completion:^(BOOL finished) {
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
效果如下:
我們發(fā)現(xiàn)在dismiss時(shí)衰琐,所采取的方式仍然是系統(tǒng)的方式,所以我們要修改它們只需要實(shí)現(xiàn)animationControllerForDismissedController
就可以了炼蹦,其實(shí)是和上面的一樣的羡宙。 繼續(xù)重寫方法animateTransition:
的實(shí)現(xiàn)。
if (self.presenting) {
UIView *containerView = [transitionContext containerView];
UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
CGRect initialFrame = self.originFrame;
CGRect finalFrame = toView.frame;
CGFloat xScaleFactor = initialFrame.size.width/finalFrame.size.width;
CGFloat yScaleFactor = initialFrame.size.height/finalFrame.size.height;
CGAffineTransform scaleTransform = CGAffineTransformMakeScale(xScaleFactor, yScaleFactor);
toView.transform = scaleTransform;
toView.center = CGPointMake(CGRectGetMidX(initialFrame)*0.5, CGRectGetMidY(initialFrame)*0.5);
[containerView addSubview:toView];
[UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0 usingSpringWithDamping:0.4 initialSpringVelocity:0.0 options:0 animations:^{
toView.transform = CGAffineTransformIdentity;
toView.center = CGPointMake(CGRectGetMidX(finalFrame), CGRectGetMidY(finalFrame));
} completion:^(BOOL finished) {
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
else{
UIView *containerView = [transitionContext containerView];
UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
[containerView addSubview:toView];
//添加toView之后掐隐,fromView將看不到狗热,所以將其移到前面
[containerView bringSubviewToFront:fromView];
[UIView animateWithDuration:2 delay:0 usingSpringWithDamping:0.4 initialSpringVelocity:0 options:0 animations:^{
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformScale(transform, 0.1, 0.1);
fromView.transform = transform;
} completion:^(BOOL finished) {
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
效果如下:
源代碼地址在這里