各視圖控制器之間的切換肥橙,大概可分為三種
- UITabBarController 他的子視圖控制器被選中時(shí)涯塔,切換子視圖
- UINavigationController push/pop一個(gè)子視圖時(shí)
- 一個(gè)UIViewController 被presented/dismissed時(shí)
自定義非交互式過渡動(dòng)畫
通用部分
1每窖、所要實(shí)現(xiàn)自定義過渡動(dòng)畫的ViewController實(shí)現(xiàn)對(duì)應(yīng)的delegate扣唱,UITabBarViewController對(duì)應(yīng)的是UITabBarControllerDelegate
篙悯,UINavigationController對(duì)應(yīng)的是UINavigationControllerDelegate
矮冬。
2、在動(dòng)畫開始時(shí)柠偶,delegate會(huì)調(diào)用對(duì)應(yīng)的動(dòng)畫控制器情妖,也就是實(shí)現(xiàn)了UIViewControllerAnimatedTransitioning
協(xié)議的類,這個(gè)類可以是任意類型诱担。返回nil毡证,則使用默認(rèn)動(dòng)畫效果。
3蔫仙、動(dòng)畫控制器實(shí)現(xiàn)對(duì)應(yīng)的方法
- transitionDuration:
動(dòng)畫執(zhí)行時(shí)間 - animateTransition:
實(shí)現(xiàn)自定義動(dòng)畫的地方
其他解釋在代碼注釋中
實(shí)現(xiàn)一個(gè)簡(jiǎn)單的例子料睛,將UITabBarController的切換視圖方式改成左右移動(dòng)的方式
Swift版本:
只是個(gè)簡(jiǎn)單的例子,所以建立一個(gè) Tabbled Application 模板的項(xiàng)目
實(shí)現(xiàn)AppDelegate即可
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
(self.window!.rootViewController as! UITabBarController).delegate = self
return true
}
}
extension AppDelegate : UITabBarControllerDelegate{
func tabBarController(tabBarController: UITabBarController, animationControllerForTransitionFromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return self
}}
extension AppDelegate : UIViewControllerAnimatedTransitioning{
//動(dòng)畫的執(zhí)行時(shí)間摇邦,需要注意的是一定要和animateTransition:里的動(dòng)畫時(shí)間保持一致
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return 0.4
}
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
let vc1 = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!
let vc2 = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
let containerView = transitionContext.containerView()!
//這個(gè)方法是iOS8之后才有的
let view1 = transitionContext.viewForKey(UITransitionContextFromViewKey)!
let view2 = transitionContext.viewForKey(UITransitionContextToViewKey)!
//iOS8之前取得的方式
// let view11 = vc1.view
// let view22 = vc2.view
let r1start = transitionContext.initialFrameForViewController(vc1)
let r2end = transitionContext.finalFrameForViewController(vc2)
//which key we are going depends on which vc is which the most general way to express this is in terms of index number
let tab = self.window!.rootViewController as! UITabBarController
let index1 = tab.viewControllers!.indexOf(vc1)
let index2 = tab.viewControllers!.indexOf(vc2)
let dir : CGFloat = index1 < index2 ? 1 : -1
var r1end = r1start
r1end.origin.x -= r1end.width * dir
var r2start = r2end
r2start.origin.x += r2start.width * dir
view2.frame = r2start
containerView.addSubview(view2)
//避免其他手勢(shì)事件的干擾
UIApplication.sharedApplication().beginIgnoringInteractionEvents()
//執(zhí)行具體動(dòng)畫恤煞,不同效果的代碼不同處就是在這了
UIView.animateWithDuration(0.4, animations: {
view1.frame = r1end
view2.frame = r2end
}) { (_) in
//防止取消狀態(tài)
transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
UIApplication.sharedApplication().endIgnoringInteractionEvents()
}
}
func animationEnded(transitionCompleted: Bool) {
if transitionCompleted {
print("Completed")
}else{
print("NO Completed")
}
}
}
為了讓代碼顯得整潔一些,并且可以是該動(dòng)畫可以在多出更方便的使用施籍,我們將其獨(dú)立出來居扒。
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
(self.window!.rootViewController as! UITabBarController).delegate = self
return true
}
}
extension AppDelegate : UITabBarControllerDelegate{
func tabBarController(tabBarController: UITabBarController, animationControllerForTransitionFromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
let animation = CustomTransitionAimation()
animation.tab = (self.window!.rootViewController as! UITabBarController)
return animation
}
}
動(dòng)畫類的實(shí)現(xiàn)
import UIKit
class CustomTransitionAimation: NSObject,UIViewControllerAnimatedTransitioning {
var tab : UITabBarController?
//動(dòng)畫的執(zhí)行時(shí)間,需要注意的是一定要和animateTransition:里的動(dòng)畫時(shí)間保持一致
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return 0.4
}
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
let vc1 = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!
let vc2 = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
let containerView = transitionContext.containerView()!
//這個(gè)方法是iOS8之后才有的
let view1 = transitionContext.viewForKey(UITransitionContextFromViewKey)!
let view2 = transitionContext.viewForKey(UITransitionContextToViewKey)!
//iOS8之前取得的方式
// let view11 = vc1.view
// let view22 = vc2.view
let r1start = transitionContext.initialFrameForViewController(vc1)
let r2end = transitionContext.finalFrameForViewController(vc2)
//which key we are going depends on which vc is which the most general way to express this is in terms of index number
let tab = self.tab!
let index1 = tab.viewControllers!.indexOf(vc1)
let index2 = tab.viewControllers!.indexOf(vc2)
let dir : CGFloat = index1 < index2 ? 1 : -1
var r1end = r1start
r1end.origin.x -= r1end.width * dir
var r2start = r2end
r2start.origin.x += r2start.width * dir
view2.frame = r2start
containerView.addSubview(view2)
//避免其他手勢(shì)事件的干擾
UIApplication.sharedApplication().beginIgnoringInteractionEvents()
//執(zhí)行具體動(dòng)畫丑慎,不同效果的代碼不同處就是在這了
UIView.animateWithDuration(0.4, animations: {
view1.frame = r1end
view2.frame = r2end
}) { (_) in
//防止取消狀態(tài)
transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
UIApplication.sharedApplication().endIgnoringInteractionEvents()
}
}
func animationEnded(transitionCompleted: Bool) {
if transitionCompleted {
print("Completed")
}else{
print("NO Completed")
}
}
}
Objective-C版本
#import "AppDelegate.h"
#import "CusomTransitionAnimation.h"
@interface AppDelegate ()<UITabBarControllerDelegate>
@property(nonatomic,strong) UITabBarController *tabBarController;
@property(nonatomic,strong) CusomTransitionAnimation *customAnimation;
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
self.tabBarController = (UITabBarController *)self.window.rootViewController;
self.tabBarController.delegate = self;
return YES;
}
- (id <UIViewControllerAnimatedTransitioning> )tabBarController:(UITabBarController *)tabBarController animationControllerForTransitionFromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC
{
CusomTransitionAnimation *animation = [[CusomTransitionAnimation alloc]init];
animation.tabBarController = self.tabBarController;
return animation;
}
- (CusomTransitionAnimation *)customAnimation
{
if (!_customAnimation) {
_customAnimation = [[CusomTransitionAnimation alloc]init];
_customAnimation.tabBarController = self.tabBarController;
}
return _customAnimation;
}
@end
動(dòng)畫類
//.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface CusomTransitionAnimation : NSObject<UIViewControllerAnimatedTransitioning>
@property(nonatomic,strong) UITabBarController *tabBarController;
@end
//.m
#import "CusomTransitionAnimation.h"
@implementation CusomTransitionAnimation
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext
{
return 0.4;
}
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
{
UIView *containerView = [transitionContext containerView];
UIViewController *vc1 = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *vc2 = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *view1 = [transitionContext viewForKey:UITransitionContextFromViewKey];
UIView *view2 = [transitionContext viewForKey:UITransitionContextToViewKey];
CGRect vc1start = [transitionContext initialFrameForViewController:vc1];
CGRect vc2end = [transitionContext finalFrameForViewController:vc2];
NSUInteger index1 = [self.tabBarController.viewControllers indexOfObject:vc1];
NSUInteger index2 = [self.tabBarController.viewControllers indexOfObject:vc2];
int dir = index1 < index2 ? 1 : -1;
CGRect vc1end = vc1start;
vc1end.origin.x -= vc1end.size.width * dir;
CGRect vc2start = vc2end;
vc2start.origin.x += vc2start.size.width * dir;
view2.frame = vc2start;
[containerView addSubview:view2];
[[UIApplication sharedApplication]beginIgnoringInteractionEvents];
[UIView animateWithDuration:0.4 animations:^{
view1.frame = vc1end;
view2.frame = vc2end;
} completion:^(BOOL finished) {
[transitionContext completeTransition:(!transitionContext.transitionWasCancelled)];
[[UIApplication sharedApplication]endIgnoringInteractionEvents];
}];
}
- (void)animationEnded:(BOOL)transitionCompleted
{
if (transitionCompleted) {
NSLog(@"Completed");
}else{
NSLog(@"No Completed");
}
}
@end
UINavigationController實(shí)現(xiàn)是類似的喜喂,只不過調(diào)用動(dòng)畫類的方式不同
viewController實(shí)現(xiàn)
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.navigationController!.delegate = self
}
@IBAction func pushAction(sender: AnyObject) {
let second = SecondViewController()
self.navigationController!.pushViewController(second, animated: true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension ViewController : UINavigationControllerDelegate{
func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
//operation 用來區(qū)分是push還是pop操作
if operation == .Push {
return CustomPushTransitionAimation()
}
else if operation == .Pop{
return CustomPopTransitionAimation()
}
return nil
}
}
Push動(dòng)畫
import UIKit
class CustomPushTransitionAimation: NSObject,UIViewControllerAnimatedTransitioning {
//動(dòng)畫的執(zhí)行時(shí)間,需要注意的是一定要和animateTransition:里的動(dòng)畫時(shí)間保持一致
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return 0.4
}
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
// let vc1 = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!
// let vc2 = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
let containerView = transitionContext.containerView()!
//這個(gè)方法是iOS8之后才有的
let view1 = transitionContext.viewForKey(UITransitionContextFromViewKey)!
let view2 = transitionContext.viewForKey(UITransitionContextToViewKey)!
//iOS8之前取得的方式
// let view11 = vc1.view
// let view22 = vc2.view
view2.transform = CGAffineTransformMakeScale(0.1, 0.1)
containerView.addSubview(view2)
//避免其他手勢(shì)事件的干擾
UIApplication.sharedApplication().beginIgnoringInteractionEvents()
//執(zhí)行具體動(dòng)畫竿裂,不同效果的代碼不同處就是在這了
UIView.animateWithDuration(0.4, animations: {
view2.transform = CGAffineTransformIdentity
}) { (_) in
//防止取消狀態(tài)
transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
UIApplication.sharedApplication().endIgnoringInteractionEvents()
}
}
func animationEnded(transitionCompleted: Bool) {
if transitionCompleted {
print("Completed")
}else{
print("NO Completed")
}
}
}
Pop動(dòng)畫
import UIKit
class CustomPushTransitionAimation: NSObject,UIViewControllerAnimatedTransitioning {
//動(dòng)畫的執(zhí)行時(shí)間玉吁,需要注意的是一定要和animateTransition:里的動(dòng)畫時(shí)間保持一致
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return 0.4
}
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
// let vc1 = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!
// let vc2 = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
let containerView = transitionContext.containerView()!
//這個(gè)方法是iOS8之后才有的
let view1 = transitionContext.viewForKey(UITransitionContextFromViewKey)!
let view2 = transitionContext.viewForKey(UITransitionContextToViewKey)!
//iOS8之前取得的方式
// let view11 = vc1.view
// let view22 = vc2.view
view2.transform = CGAffineTransformMakeScale(0.1, 0.1)
containerView.addSubview(view2)
//避免其他手勢(shì)事件的干擾
UIApplication.sharedApplication().beginIgnoringInteractionEvents()
//執(zhí)行具體動(dòng)畫,不同效果的代碼不同處就是在這了
UIView.animateWithDuration(0.4, animations: {
view2.transform = CGAffineTransformIdentity
}) { (_) in
//防止取消狀態(tài)
transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
UIApplication.sharedApplication().endIgnoringInteractionEvents()
}
}
func animationEnded(transitionCompleted: Bool) {
if transitionCompleted {
print("Completed")
}else{
print("NO Completed")
}
}
}
可交互的自定義過渡動(dòng)畫
有兩種實(shí)現(xiàn)方式
使用百分比驅(qū)動(dòng)
使用這種方式比較簡(jiǎn)單铛绰,還是按照上述的方法先實(shí)現(xiàn)不可交互的自定義動(dòng)畫诈茧。接下來實(shí)現(xiàn)回調(diào)方法
func tabBarController(tabBarController: UITabBarController, interactionControllerForAnimationController animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning?
主要是UIPercentDrivenInteractiveTransition
的使用,實(shí)質(zhì)是frozen animation捂掰。
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var rightEdgr : UIScreenEdgePanGestureRecognizer!
var leftEdgr : UIScreenEdgePanGestureRecognizer!
var inter : UIPercentDrivenInteractiveTransition!//這個(gè)類是關(guān)鍵
var interacting = false
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
let tab = (self.window!.rootViewController as! UITabBarController)
tab.delegate = self
let sep = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(pan))
sep.edges = .Right
sep.delegate = self
tab.view.addGestureRecognizer(sep)
self.rightEdgr = sep
let sep2 = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(pan))
sep2.edges = .Left
sep2.delegate = self
tab.view.addGestureRecognizer(sep2)
self.leftEdgr = sep2
return true
}
}
extension AppDelegate : UITabBarControllerDelegate{
func tabBarController(tabBarController: UITabBarController, animationControllerForTransitionFromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
// let animation = CustomTransitionAimation()
// animation.tab = (self.window!.rootViewController as! UITabBarController)
// return animation
return self
}
func tabBarController(tabBarController: UITabBarController, interactionControllerForAnimationController animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
if self.interacting {
return self.inter
}
return nil
}
}
extension AppDelegate : UIGestureRecognizerDelegate{
func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {
let tab = self.window!.rootViewController as! UITabBarController
var result = false
if gestureRecognizer == self.rightEdgr {
result = (tab.selectedIndex < tab.viewControllers!.count - 1)
}
else{
result = tab.selectedIndex > 0
}
return result
}
func pan(g : UIScreenEdgePanGestureRecognizer) -> Void {
//重點(diǎn)需要處理的地方
let v = g.view!
let tab = self.window!.rootViewController as! UITabBarController
let delta = g.translationInView(v)
let percent = fabs(delta.x/v.bounds.width)
switch g.state {
case .Began:
self.inter = UIPercentDrivenInteractiveTransition()
self.interacting = true
if g == self.rightEdgr {
tab.selectedIndex = tab.selectedIndex + 1
}else{
tab.selectedIndex = tab.selectedIndex - 1
}
print("selectedIndex = \(tab.selectedIndex)")
case .Changed:
self.inter.updateInteractiveTransition(percent)
case .Ended:
if percent > 0.5 {
self.inter.finishInteractiveTransition()//調(diào)用該方法敢会,將快速結(jié)束之前所設(shè)置的操作
}else{
self.inter.cancelInteractiveTransition()//調(diào)用該方法,將恢復(fù)原狀
}
self.interacting = false
case .Cancelled:
self.inter.cancelInteractiveTransition()
self.interacting = false
default : break
}
}
}
extension AppDelegate : UIViewControllerAnimatedTransitioning{
//動(dòng)畫的執(zhí)行時(shí)間这嚣,需要注意的是一定要和animateTransition:里的動(dòng)畫時(shí)間保持一致
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return 0.4
}
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
let vc1 = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!
let vc2 = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
let containerView = transitionContext.containerView()!
//這個(gè)方法是iOS8之后才有的
let view1 = transitionContext.viewForKey(UITransitionContextFromViewKey)!
let view2 = transitionContext.viewForKey(UITransitionContextToViewKey)!
//iOS8之前取得的方式
// let view11 = vc1.view
// let view22 = vc2.view
let r1start = transitionContext.initialFrameForViewController(vc1)
let r2end = transitionContext.finalFrameForViewController(vc2)
//which key we are going depends on which vc is which the most general way to express this is in terms of index number
let tab = self.window!.rootViewController as! UITabBarController
let index1 = tab.viewControllers!.indexOf(vc1)
let index2 = tab.viewControllers!.indexOf(vc2)
let dir : CGFloat = index1 < index2 ? 1 : -1
var r1end = r1start
r1end.origin.x -= r1end.width * dir
var r2start = r2end
r2start.origin.x += r2start.width * dir
view2.frame = r2start
containerView.addSubview(view2)
/*
//避免其他手勢(shì)事件的干擾
UIApplication.sharedApplication().beginIgnoringInteractionEvents()
//執(zhí)行具體動(dòng)畫鸥昏,不同效果的代碼不同處就是在這了
UIView.animateWithDuration(0.4, animations: {
view1.frame = r1end
view2.frame = r2end
}) { (_) in
//防止取消狀態(tài)
transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
UIApplication.sharedApplication().endIgnoringInteractionEvents()
}
*/
let opts : UIViewAnimationOptions = self.interacting ? .CurveLinear : []
if !self.interacting {
UIApplication.sharedApplication().beginIgnoringInteractionEvents()
}
UIView.animateWithDuration(0.4, delay: 0, options: opts, animations: {
view1.frame = r1end
view2.frame = r2end
}) { (_) in
let canceld = transitionContext.transitionWasCancelled()
transitionContext.completeTransition(!canceld)
if UIApplication.sharedApplication().isIgnoringInteractionEvents(){
UIApplication.sharedApplication().endIgnoringInteractionEvents()
}
}
}
func animationEnded(transitionCompleted: Bool) {
if transitionCompleted {
print("Completed")
}else{
print("NO Completed")
}
let tab = self.window!.rootViewController as! UITabBarController
print(tab.selectedIndex)
}
}
不使用百分比驅(qū)動(dòng)
這種寫法雖然看起來會(huì)稍微復(fù)雜一些,但是他的可定制程度更高姐帚,因?yàn)槲覀兛梢灾苯訉?duì)fromView 和 toView等進(jìn)行直接操作吏垮。
不在需要具體實(shí)現(xiàn)animateTransition
方法,但是他還是要存在的,因?yàn)樗膮f(xié)議必須實(shí)現(xiàn)的方法膳汪。
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
}
將其中的代碼放到實(shí)現(xiàn)了協(xié)議UIViewControllerInteractiveTransitioning
的類中實(shí)現(xiàn)唯蝶。放到方法
func startInteractiveTransition(transitionContext: UIViewControllerContextTransitioning){
}
中就可以了。
這次使用OC實(shí)現(xiàn)遗嗽,都熟悉下:
#import "AppDelegate.h"
@interface AppDelegate ()<UIViewControllerAnimatedTransitioning,UIViewControllerInteractiveTransitioning,UITabBarControllerDelegate,UIGestureRecognizerDelegate>
@property(nonatomic,strong) id<UIViewControllerContextTransitioning>transitionContext;
@property(nonatomic,strong) UIScreenEdgePanGestureRecognizer *leftEdgr;
@property(nonatomic,strong) UIScreenEdgePanGestureRecognizer *rightEdgr;
@property(nonatomic,assign) BOOL interacting;
@property(nonatomic,assign) CGRect r1end;
@property(nonatomic,assign) CGRect r2start;
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
UITabBarController *tab = (UITabBarController *)self.window.rootViewController;
tab.delegate = self;
self.leftEdgr = ({
UIScreenEdgePanGestureRecognizer *pan = [[UIScreenEdgePanGestureRecognizer alloc]initWithTarget:self action:@selector(pan:)];
pan.edges = UIRectEdgeLeft;
pan.delegate = self;
[tab.view addGestureRecognizer:pan];
pan;
});
self.rightEdgr = ({
UIScreenEdgePanGestureRecognizer *pan = [[UIScreenEdgePanGestureRecognizer alloc]initWithTarget:self action:@selector(pan:)];
pan.edges = UIRectEdgeRight;
pan.delegate = self;
[tab.view addGestureRecognizer:pan];
pan;
});
self.r1end = CGRectZero;
self.r2start = CGRectZero;
return YES;
}
#pragma mark - Actions
- (void)pan:(UIScreenEdgePanGestureRecognizer *)ges
{
UIView *gesView = ges.view;
UITabBarController *tab = (UITabBarController *)self.window.rootViewController;
CGPoint delta = [ges translationInView:gesView];
CGFloat percent = delta.x/gesView.bounds.size.width;
UIViewController *fromVC,*toVC;
UIView *fromView,*toView;
CGRect fromRectStart,toRectEnd;
if (self.transitionContext) {
fromVC = [self.transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
toVC = [self.transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
fromView = [self.transitionContext viewForKey:UITransitionContextFromViewKey];
toView = [self.transitionContext viewForKey:UITransitionContextToViewKey];
fromRectStart = [self.transitionContext initialFrameForViewController:fromVC];
toRectEnd = [self.transitionContext finalFrameForViewController:toVC];
}
switch (ges.state) {
case UIGestureRecognizerStateBegan: {
self.interacting = YES;
if (ges == self.leftEdgr) {
tab.selectedIndex = tab.selectedIndex - 1;
}else{
tab.selectedIndex = tab.selectedIndex + 1;
}
break;
}
case UIGestureRecognizerStateChanged:{
//可以在這里對(duì)fromView 和 toView 進(jìn)行直接操作粘我,所以,可定制程度高
fromRectStart.origin.x += (self.r1end.origin.x - fromRectStart.origin.x) * percent;
fromView.frame = fromRectStart;
NSLog(@"fromRect : %@",NSStringFromCGRect(fromRectStart));
CGRect toRectStart = self.r2start;
toRectStart.origin.x += (toRectStart.origin.x - self.r2start.origin.x) * percent;
toView.frame = toRectStart;
NSLog(@"toRect : %@",NSStringFromCGRect(toRectStart));
//在UITabBarController的切換中可能看不出來這句話有什么用處痹换,但是在UINavigationController中這個(gè)就很有用了征字,可以自己處理NavigationBar的效果等。
[self.transitionContext updateInteractiveTransition:percent];
break;
}
case UIGestureRecognizerStateEnded: {
if (percent > 0.5) {
//達(dá)到要求娇豫,設(shè)置成最終狀態(tài)
[UIView animateWithDuration:0.2 animations:^{
fromView.frame = self.r1end;
toView.frame = toRectEnd;
} completion:^(BOOL finished) {
[self.transitionContext finishInteractiveTransition];
[self.transitionContext completeTransition:YES];
}];
}
else{
//恢復(fù)原狀
[UIView animateWithDuration:0.2 animations:^{
fromView.frame = fromRectStart;
toView.frame = self.r2start;
} completion:^(BOOL finished) {
[self.transitionContext cancelInteractiveTransition];
[self.transitionContext completeTransition:NO];
}];
}
self.interacting = NO;
self.transitionContext = nil;
break;
}
case UIGestureRecognizerStateCancelled: {
//恢復(fù)原狀
fromView.frame = fromRectStart;
toView.frame = self.r2start;
[self.transitionContext finishInteractiveTransition];
[self.transitionContext completeTransition:NO];
self.interacting = NO;
self.transitionContext = nil;
break;
}
default:
break;
}}
#pragma mark - UIGestureRecognizerDelegate
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
UITabBarController *tab = (UITabBarController *)self.window.rootViewController;
BOOL result = NO;
if (gestureRecognizer == self.leftEdgr) {
result = tab.selectedIndex > 0;
}else{
result = tab.selectedIndex < tab.viewControllers.count - 1;
}
return result;
}
#pragma mark - UITabBarControllerDelegate
- (nullable id <UIViewControllerInteractiveTransitioning>)tabBarController:(UITabBarController *)tabBarController
interactionControllerForAnimationController: (id <UIViewControllerAnimatedTransitioning>)animationController
{
return self.interacting ? self : nil;
}
- (nullable id <UIViewControllerAnimatedTransitioning>)tabBarController:(UITabBarController *)tabBarController
animationControllerForTransitionFromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC
{
return self.interacting ? self : nil;
}
#pragma mark - UIViewControllerAnimatedTransitioning
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
return 0.4;
}
//這個(gè)要實(shí)現(xiàn)匙姜,但是要保持為空
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
}
#pragma mark - UIViewControllerInteractiveTransitioning
//這個(gè)其實(shí)就是要實(shí)現(xiàn) animateTransition: 原來實(shí)現(xiàn)的功能
- (void)startInteractiveTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
self.transitionContext = transitionContext;
//即最終要達(dá)到的狀態(tài)
UIView *containerView = [transitionContext containerView];
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
// UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
CGRect fromRectStart = [transitionContext initialFrameForViewController:fromVC];
CGRect toRectEnd = [transitionContext finalFrameForViewController:toVC];
UITabBarController *tab = (UITabBarController *)self.window.rootViewController;
NSUInteger fromIndex = [tab.viewControllers indexOfObject:fromVC];
NSUInteger toIndex = [tab.viewControllers indexOfObject:toVC];
int dir = fromIndex < toIndex ? 1 : -1;
CGRect fromRectEnd = fromRectStart;
fromRectEnd.origin.x -= fromRectEnd.size.width * dir;
CGRect toRectStart = toRectEnd;
toRectStart.origin.x += toRectStart.size.width * dir;
toView.frame = toRectStart;
[containerView addSubview:toView];
self.r1end = fromRectEnd;
self.r2start = toRectStart;
}
@end
自定義Presented ViewController的過渡動(dòng)畫
實(shí)現(xiàn)主要要兩種方式
- 不使用presentation controller
- 使用presentation controller
不使用presentation controller
需要注意的地方是
- 協(xié)議transitioningDelegate聲明的位置
- 區(qū)分present 和 dismiss
- 只有當(dāng)modalPresentationStyle *不是 .FullScreen *的時(shí)候,才能使用如下方式判定是present還是dismiss
上代碼
import UIKit
class ViewController2: UIViewController {
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
//這個(gè)不能再 viewDidLoad 里執(zhí)行冯痢,太晚了
self.transitioningDelegate = self
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.transitioningDelegate = self
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.view.backgroundColor = UIColor.blueColor()
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
self.presentingViewController!.dismissViewControllerAnimated(true, completion:
nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension ViewController2 : UIViewControllerTransitioningDelegate{
func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return self
}
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return self
}
}
extension ViewController2 : UIViewControllerAnimatedTransitioning{
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return 0.4
}
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
let containerView = transitionContext.containerView()!
//For a presentation that is not .FullScreen,the unused view is nil.
let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)
let toView = transitionContext.viewForKey(UITransitionContextToViewKey)
//distinguish the cases
//dismiss
if fromView != nil {
UIView.animateWithDuration(0.4, animations: {
fromView!.transform = CGAffineTransformMakeScale(0.1, 0.1)
fromView!.alpha = 0
}, completion: { (_) in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
})
}
//presenting
else if toView != nil{
let toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
let toRectEnd = transitionContext.finalFrameForViewController(toVC)
toView!.frame = CGRectInset(toRectEnd, 40, 40)
toView!.transform = CGAffineTransformMakeScale(0.1, 0.1)
toView!.alpha = 0
containerView.addSubview(toView!)
//also can modify the containerView
// containerView.backgroundColor = UIColor.greenColor()
UIView.animateWithDuration(0.4, animations: {
toView!.transform = CGAffineTransformIdentity
toView!.alpha = 1.0
}) { (_) in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
}
}
}
}
在.FullScreen 類型的presented中氮昧,會(huì)取得presenting View,并將其添加到containerView中系羞,其他類型的則是郭计,containerView是在presenting view之前的,不能給containerView添加動(dòng)畫椒振,并且containerView不會(huì)移除。
使用Presentation Controller
在此之前梧乘,需要理解的一些東西
- Animation Controller 用來負(fù)責(zé)動(dòng)畫效果澎迎,也就是presented View 如何移動(dòng)到最終目標(biāo)位置
- Presentation Controller 確定presented View的最終位置,還有向containerView上添加一些視圖选调,例如半透明效果什么的夹供。
舉個(gè)例子來說明,如果只是實(shí)現(xiàn)了UIPresentationController 而沒有做上面的那些工作仁堪,會(huì)使用默認(rèn)動(dòng)畫彈出哮洽。
接下來說說實(shí)現(xiàn):
在實(shí)現(xiàn)上面效果的基礎(chǔ)上,需要在做幾點(diǎn)工作
-
在設(shè)置presented viewController的 transitioningDelegate屬性的同時(shí)弦聂,還需要設(shè)置其的modalPresentationStyle為.Custom鸟辅,這個(gè)是必須的。(注意代碼位置)
// NB if we want to modify the _animation_, we need to set the transitioningDelegate self.transitioningDelegate = self // if we want to modify the _presentation_, we need to set the style to custom // customize presentation only on iPhone // how will we find out which it is? we have no traitCollection yet... // I know, let's ask the window if UIApplication.sharedApplication().keyWindow!.traitCollection.userInterfaceIdiom == .Phone { self.modalPresentationStyle = .Custom }
-
在實(shí)現(xiàn)另一個(gè)方法
@available(iOS 8.0, *) optional public func presentationControllerForPresentedViewController(presented: UIViewController, presentingViewController presenting: UIViewController, sourceViewController source: UIViewController) -> UIPresentationController?
接下來便是在 UIPresentationController的子類中重載他的方法來達(dá)到我們所要的效果
方法的說明:
// Position of the presented view in the container view by the end of the presentation transition.
// (Default: container view bounds)
public func frameOfPresentedViewInContainerView() -> CGRect
返回presented view的最終位置
使用下面的方法來給containerView添加或移除一些額外的視圖
presentationTransitionWillBegin
presentationTransitionDidEnd
dismissalTransitionWillBegin
dismissalTransitionDidEnd
使用下面方法來個(gè)添加的額外視圖進(jìn)行布局
containerViewWillLayoutSubviews
containerViewDidLayoutSubviews
方法
shouldPresentInFullscreen
默認(rèn)返回的是true莺葫,如果返回false匪凉,會(huì)將presentation改變?yōu)?.CurrentContext
主要就這幾個(gè),完結(jié)捺檬。
Transition Coordinator的使用
主要注意幾個(gè)方法的使用就可以了
-
animateAlongsideTransition:completion:
可以用來給已經(jīng)在視圖上的子視圖添加動(dòng)畫效果- (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; if (self.transitionCoordinator != nil) { [self.transitionCoordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) { NSLog(@"Transition Animation"); self.animationView.center = self.view.center; } completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) { NSLog(@"Transition completion"); }]; } else{ NSLog(@"Transition is NULL"); } }
notifyWhenInteractionEndsUsingBlock:
監(jiān)控視圖返回時(shí)手勢(shì)動(dòng)作是否成功再层。比如在UINavigationController中左側(cè)滑動(dòng)返回時(shí),需要在上一個(gè)ViewController中實(shí)現(xiàn)。
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (self.transitionCoordinator != nil) {
[self.transitionCoordinator notifyWhenInteractionEndsUsingBlock:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
if ([context isCancelled]) {
NSLog(@"1 Interaction Cancelled");
return ;
}
NSLog(@"1 Interaction Ends");
}];
}
else{
NSLog(@"1 Transition is NULL");
}
}