在IOS7之前,View Controller的切換重要有4種:
IOS5,挪用- (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種辦法這裡就不多說了,很罕見的體系辦法.至於第四種,我在後面文章-分析網易標簽欄的後果中曾經做了論述,然則它供給的容器轉場動畫只可以完成一些簡略的UIView動畫,然則難以重用,耦合高.
(2)症結的API:
A.動畫掌握器 (Animation Controllers) 服從 UIViewControllerAnimatedTransitioning 協定,而且擔任現實履行動畫。
B.交互掌握器 (Interaction Controllers) 經由過程服從 UIViewControllerInteractiveTransitioning 協定來掌握可交互式的轉場。
C.轉場署理 (Transitioning Delegates) 依據分歧的轉場類型便利的供給須要的動畫掌握器和交互掌握器。
個中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) 界說了轉場時須要的元數據,好比在轉場進程中所介入的視圖掌握器和視圖的相干屬性。 轉場高低文對象服從 UIViewControllerContextTransitioning 協定,而且這是由體系擔任生成和供給的。
E.轉場調和器(Transition Coordinators) 可以在運轉轉場動畫時,並行的運轉其他動畫。 轉場調和器服從 UIViewControllerTransitionCoordinator 協定。
(3)新的API重要供給了2種VC切換的方法:
A.非交互式切換,即界說一種從一個VC到另外一個VC的動畫後果,切換的時刻主動播放,
B.交互式切換,這類方法異樣須要界說動畫後果,只是這個動畫後果會依據追隨交互式手勢來切換VC並同時播放動畫後果。iOS7供給了一個默許的基於百分比的動畫完成 UIPercentDrivenInteractiveTransition,並且依據WWDC的解釋,最簡略的完成交互式動畫的辦法就是經由過程繼續 UIPercentDrivenInteractiveTransition。
蘋果給我們開辟者供給的是都是協定接口,所以我們可以或許很好的零丁提出來寫成一個個類,在外面完成我們各類自界說後果.
(4)來看看完成UIViewControllerAnimatedTransitioning的自界說動畫類
/**
* 自界說的動畫類
* 完成協定------>@protocol UIViewControllerAnimatedTransitioning
* 這個接口擔任切換的詳細內容,也即“切換中應當產生甚麼”
*/
@interface MTHCustomAnimator : NSObject <UIViewControllerAnimatedTransitioning>
@end
@implementation MTHCustomAnimator
// 體系給出一個切換高低文,我們依據高低文情況前往這個切換所須要的消費時光
- (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 = CGAff.netransformMakeTranslation(-320, 0);
toViewController.view.alpha = 1.0;
} completion:^(BOOL finished) {
fromViewController.view.transform = CGAff.netransformIdentity;
// 聲明過渡停止-->記住,必定別忘了在過渡停止時挪用 completeTransition: 這個辦法
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
PS:從協定中兩個辦法可以看出,下面兩個必需完成的辦法須要一個轉場高低文參數,這是一個服從UIViewControllerContextTransitioning 協定的對象。平日情形下,當我們應用體系的類時,體系框架為我們供給的轉場署理(Transitioning Delegates),為我們創立了轉場高低文對象,並把它傳遞給動畫掌握器。
// MainViewController
@interface MTHMainViewController () <UINavigationControllerDelegate,UIViewControllerTransitioningDelegate>
@property (nonatomic,strong) MTHCustomAnimator *customAnimator;
@property (nonatomic,strong) PDTransitionAnimator *minToMaxAnimator;
@property (nonatomic,strong) MTHNextViewController *nextVC;
// 交互掌握器 (Interaction Controllers) 經由過程服從 UIViewControllerInteractiveTransitioning 協定來掌握可交互式的轉場。
@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];
// 完成交互操作的手勢
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
* 交互式轉場,自我懂得意思是,用戶能經由過程本身的舉措來(罕見:手勢)掌握,分歧於體系缺省給定的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;
}
以上完成的長短交互的轉場,指的是完整依照體系指定的切換機制,用戶沒法半途撤消或許掌握進度切換.那怎樣來完成交互轉場呢:
UIPercentDrivenInteractiveTransition完成了UIViewControllerInteractiveTransitioning接口的類,,可以用一個百分比來掌握交互式切換的進程。我們在手勢辨認中只須要告知這個類的實例以後的狀況百分好比何,體系便依據這個百分比和我們之前設定的遷徙方法為我們盤算以後應當的UI襯著,非常便利。詳細的幾個主要辦法:
-(void)updateInteractiveTransition:(CGFloat)percentComplete 更新百分比,普通經由過程手勢辨認的長度之類的來盤算一個值,然落後行更新。以後的例子裡會看到具體的用法
-(void)cancelInteractiveTransition 申報交互撤消,前往切換前的狀況
–(void)finishInteractiveTransition 申報交互完成,更新到切換後的狀況
#pragma mark - 手勢交互的重要完成--->UIPercentDrivenInteractiveTransition
- (void)didClickPanGestureRecognizer:(UIPanGestureRecognizer*)recognizer
{
UIView* view = self.view;
if (recognizer.state == UIGestureRecognizerStateBegan) {
// 獲得手勢的觸摸點坐標
CGPoint location = [recognizer locationInView:view];
// 斷定,用戶從右半邊滑動的時刻,推出下一個VC(依據現實須要是推動照樣推出)
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];
// 依據手指拖動的間隔盤算一個百分比,切換的動畫後果也跟著這個百分比來走
CGFloat distance = fabs(translation.x / CGRectGetWidth(view.bounds));
// 交互掌握器掌握動畫的進度
[self.interactionController updateInteractiveTransition:distance];
} else if (recognizer.state == UIGestureRecognizerStateEnded) {
CGPoint translation = [recognizer translationInView:view];
// 依據手指拖動的間隔盤算一個百分比,切換的動畫後果也跟著這個百分比來走
CGFloat distance = fabs(translation.x / CGRectGetWidth(view.bounds));
// 挪動跨越一半就強迫完成
if (distance > 0.5) {
[self.interactionController finishInteractiveTransition];
} else {
[self.interactionController cancelInteractiveTransition];
}
// 停止後必定要置為nil
self.interactionController = nil;
}
}
最初,給年夜家分享一個動畫殊效:相似於飛兔雲傳的發送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做動畫價值太高.由於是截取了曾經存在的內容,這個辦法只能反響出這個被截取的view以後的狀況信息,而不克不及反響這個被截取的view今後要顯示的信息.但是,不論怎樣樣,挪用這個辦法都邑比將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的快照有以下步調: 起首創立一個UIGraphics的圖象高低文,然後將視圖的layer襯著到該高低文中,從而獲得一個圖象,最初封閉圖象高低文,並將圖象顯示在UIImageView中。如今我們只須要一行代碼便可以完成上述步調了:
[view snapshotViewAfterScreenUpdates:NO];
這個辦法制造了一個UIView的正本,假如我們願望視圖在履行動畫之前保留如今的外不雅,以備以後應用(動畫中視圖能夠會被子視圖隱瞞或許產生其他一些變更),該辦法就特殊便利。
afterUpdates參數表現能否在一切後果運用在視圖上了今後再獲得快照。例如,假如該參數為NO,則立馬獲得該視圖如今狀況的快照,反之,以下代碼只能獲得一個空白快照:
[view snapshotViewAfterScreenUpdates:YES];
[view setAlpha:0.0];
因為我們設置afterUpdates參數為YES,而視圖的通明度值被設置成了0,所以辦法將在該設置運用在視圖上了以後才停止快照,因而乎屏幕一無所有。別的就是……你可以對快照再停止快照……持續快照……
持續後面的內容,這一章,重要引見自界說ViewController容器上視圖VC的切換.先來看看體系給我們供給的容器掌握器 UINavigationController和UITabBarController 都有一個NSArray類型的屬性viewControllers,很顯著,存儲的就是須要切換的視圖VC.同理,我們界說一個ContainerViewController,是UIViewController的直接子類,用來作為容器依托,額,其他屬性界說詳見代碼吧,這裡不多說了.(PS:本來我停止多個自界說視圖VC切換的辦法,是放置一個UIScrollView,然後把一切childViewController的View的frame的X坐標,依此按320遞增,年夜家可以自行想象下,如許欠好的處所,我感到就是一切的VC一經加載就全體實體化了,並且不會由於被切換釀成暫不顯示而釋放失落)
偷懶下,用storyboard創立的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數組
@property (assign, nonatomic) NSInteger currentControllerIndex; // 以後選擇的VC的數組下標號
@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的數組
self.viewControllers = @[_photoSenderViewController,_videoSenderViewController,_fileSenderViewController,_contactSenderViewController,_clipboardSenderViewController];
// 缺省為下標為0的VC
self.selectedViewController = self.selectedViewController ?: self.viewControllers[0];
self.currentControllerIndex = 0;
}
照舊,完成UIViewControllerAnimatedTransitioning協定的Animator類,不外外面換個動畫後果,應用iOS7新增的彈簧動畫後果:
#import "FTMthTransitionAnimator.h"
@implementation FTMthTransitionAnimator
static CGFloat const kChildViewPadding = 16;
static CGFloat const kDamping = 0.5; // damping參數代表彈性阻尼,跟著阻尼值愈來愈接近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;
CGAff.netransform 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時,動畫將安穩加速到其終究的模子值不會振蕩。阻尼比小於1來完整停滯前將振蕩愈來愈多。可使用彈簧的初始速度,以指定的速度在模仿彈簧的端部的物體被挪動它附著之前。這是一個單位坐標系,個中1是指行駛總間隔的動畫在第二。所以,假如你轉變一個物體的地位由200PT在這個動畫,和你想要的動畫表示得似乎物體在動,在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;
// 聲明過渡停止-->記住,必定別忘了在過渡停止時挪用 completeTransition: 這個辦法。
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
@end
接上去的代碼,就是完成自界說容器切換的症結了.平日情形下,當我們應用體系內建的類時,體系框架為我們創立了轉場高低文對象,並把它傳遞給動畫掌握器。然則在我們這類情形下,我們須要自界說轉場動畫,所以我們須要承當體系框架的義務,本身去創立這個轉場高低文對象。
@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個協定辦法才成心義,可參照體系給我們界說好的交互掌握器
// @interface UIPercentDrivenInteractiveTransition : NSObject <UIViewControllerInteractiveTransitioning>
- (void)updateInteractiveTransition:(CGFloat)percentComplete {}
- (void)finishInteractiveTransition {}
- (void)cancelInteractiveTransition {}
@end
OK,預備任務都做好了,為了模仿UIScrollView的滑動切換,但又由於如今展現的長短交互式,我們界說一個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;
}
// 切換操作(自界說的,聯想我在後面文章網易標簽欄切換中,體系給的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也能夠看出,非交互轉場,其實不能完整完成UIScrollView那種分頁式的後果,依照相似百分比的情勢來停止fromVC和toVC的切換,由於我們缺乏交互掌握器.在自界說的容器中,體系是沒有供給前往交互掌握器的協定給我們的,查了蠻多材料,也沒找到給出明白的辦法,我以為,要跟完成轉場高低文一樣,模仿體系辦法,自界說的去完成交互式的協定辦法.我們就要去思慮,體系是若何搭建起這個情況的.
【iOS開辟中的ViewController轉場切換後果完成簡介】的相關資料介紹到這裡,希望對您有所幫助! 提示:不會對讀者因本文所帶來的任何損失負責。如果您支持就請把本站添加至收藏夾哦!