我正在尝试使用自定义segue在UIViewControllers之间添加一个简单的推送动画。
(不转换我的控制器以使用UINavigationController)
到目前为止找到的答案与导航控制器一起工作正常,但在不使用导航控制器时失败。 (我仍在阅读并尝试其他我在堆栈溢出时看到的答案)
我的定制segue .m感谢(ZHCustomSegue.m)Zakir于7/5/12
#import "SFCustomSegue.h"
#import "QuartzCore/QuartzCore.h"
@implementation SFCustomSegue
-(void)perform {
UIViewController *sourceViewController = (UIViewController*)[self sourceViewController];
UIViewController *destinationController = (UIViewController*)[self destinationViewController];
CATransition* transition = [CATransition animation];
transition.duration = 4.0;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromRight;
[sourceViewController.view.layer addAnimation:transition forKey:kCATransition];
[sourceViewController presentViewController:[self destinationViewController] animated:NO completion:nil];
}
如果我按照Zakir的原始示例使用导航控制器,并将最后两行替换为:
[sourceViewController.navigationController.view.layer addAnimation:transition
forKey:kCATransition];
[sourceViewController.navigationController pushViewController:destinationController animated:NO];
它有效......
Apple的医生说(注意不是导航控制器,并使用模态segue):
- (void)perform
{
// Add your own animation code here.
[[self sourceViewController] presentModalViewController:[self destinationViewController] animated:NO];
}
通过NOT Working,我没有动画。在Zakir的代码中,使用4.0秒需要4.0秒,我得到了我想要的动画。当然,如果我使用导航控制器,推送转换是默认的。在这段代码中,我没有动画,它需要0秒。
有没有人使用自定义segue动画来使用UIViewControllers?
答案 0 :(得分:2)
由于您正在调用presentViewController
,因此源视图控制器仍然存在。如果您确实想要使用目标视图控制器替换源视图控制器,以便取消分配源,则可以执行以下操作:
static UIImageView *screenShotOfView(UIView *view)
{
// Create a snapshot for animation
UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
[view.layer renderInContext:context];
UIImageView *screenShot = [[UIImageView alloc] initWithImage:UIGraphicsGetImageFromCurrentImageContext()];
UIGraphicsEndImageContext();
return screenShot;
}
- (void)perform
{
UIViewController *source = (UIViewController *) self.sourceViewController;
UIViewController *destination = (UIViewController *) self.destinationViewController;
// Swap the snapshot out for the source view controller
UIWindow *window = source.view.window;
UIImageView *screenShot = screenShotOfView(source.view);
CGRect originalFrame = destination.view.frame;
BOOL animsEnabled = [UIView areAnimationsEnabled];
[UIView setAnimationsEnabled:NO];
{
window.rootViewController = destination;
[window addSubview:screenShot];
[source.view removeFromSuperview];
CGRect frame = destination.view.frame;
frame.origin.x += source.view.bounds.size.width;
destination.view.frame = frame;
}
[UIView setAnimationsEnabled:animsEnabled];
[UIView animateWithDuration:kAnimationDuration
animations:^{
destination.view.frame = originalFrame;
CGRect frame = screenShot.frame;
frame.origin.x -= screenShot.bounds.size.width;
screenShot.frame = frame;
}
completion:^(BOOL finished) {
[screenShot removeFromSuperview];
}];
}
答案 1 :(得分:0)
看来我上面的代码“几乎”正在工作,但并不完全。 当调用最后一步时,可能是。
我的工作代码如下:
#import "SFCustomSegue.h"
#import "QuartzCore/QuartzCore.h"
@implementation SFCustomSegue
static const float delay = 0.5f;
-(void)perform {
UIViewController *sourceViewController = (UIViewController*)[self sourceViewController];
UIViewController *destinationController = (UIViewController*)[self destinationViewController];
[sourceViewController.view addSubview:destinationController.view];
CATransition* transition = [CATransition animation];
transition.duration = delay;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromRight;
[sourceViewController.view.layer addAnimation:transition forKey:kCATransition];
[self performSelector:@selector(performDone:) withObject:destinationController afterDelay:delay];
}
- (void)performDone:(id)viewController{
UIViewController *destination = (UIViewController*)viewController;
[destination.view removeFromSuperview];
[[self sourceViewController] presentViewController:destination animated:NO completion:nil];
}
@end
解决此问题的关键是添加转换视图(第13行),然后将其删除(第28行)并使用 presentViewController:animated:completion:将其添加回(第29行)。
我仍然只使用常规的CATransition转换类型“卡住”,但这对我来说已经足够了,因为我想使用 kCATransitionPush 。
答案 2 :(得分:0)
我已将两个答案中的想法合并,以获得此代码(工作正常): (上面的答案不计入设备的方向,因此它们不会在横向上工作,例如)
(带有CATransitions的变体不合适,因为两个视图之间存在间隙而另一个视图推动了另一个视图)
#import "PushSegue.h"
/*
@interface TransitionDelegate : NSObject
- (id) initWithScreenshot: (UIView*) screenshot;
@end
*/
@implementation PushSegue
- (void) perform
{
UIViewController* source = (UIViewController*) self.sourceViewController;
UIViewController* destination = (UIViewController*) self.destinationViewController;
const BOOL iPad = [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad;
float animationDuration = iPad ? 0.5 : 0.3;
// Swap the snapshot out for the source view controller
UIWindow* window = source.view.window;
UIImageView* screenShot = screenShotOfView(source.view);
// accord to device orientation
float rotation;
//NSString* transitionType;
CGRect originalFrame = destination.view.frame;
CGRect destinationFrame = destination.view.frame;
switch ([UIApplication sharedApplication].statusBarOrientation)
{
case UIInterfaceOrientationPortraitUpsideDown:
rotation = M_PI;
destinationFrame.origin.x -= source.view.bounds.size.width;
//transitionType = kCATransitionFromLeft;
break;
case UIInterfaceOrientationLandscapeLeft:
rotation = M_PI + M_PI_2;
destinationFrame.origin.y -= source.view.bounds.size.width;
//transitionType = kCATransitionFromBottom;
break;
case UIInterfaceOrientationLandscapeRight:
rotation = M_PI_2;
destinationFrame.origin.y += source.view.bounds.size.width;
//transitionType = kCATransitionFromTop;
break;
default:
rotation = 0;
destinationFrame.origin.x += source.view.bounds.size.width;
//transitionType = kCATransitionFromRight;
break;
}
screenShot.transform = CGAffineTransformMakeRotation(rotation);
// reposition after rotation
CGRect screenshotFrame = screenShot.frame;
screenshotFrame.origin.x = 0;
screenshotFrame.origin.y = 0;
screenShot.frame = screenshotFrame;
switch ([UIApplication sharedApplication].statusBarOrientation)
{
case UIInterfaceOrientationPortraitUpsideDown:
screenshotFrame.origin.x += screenShot.bounds.size.width;
break;
case UIInterfaceOrientationLandscapeLeft:
screenshotFrame.origin.y += screenShot.bounds.size.width;
break;
case UIInterfaceOrientationLandscapeRight:
screenshotFrame.origin.y -= screenShot.bounds.size.width;
break;
default:
screenshotFrame.origin.x -= screenShot.bounds.size.width;
break;
}
// swap the view with its screenshot
window.rootViewController = destination;
[window addSubview:screenShot];
[source.view removeFromSuperview];
/*
CATransition* transition = [CATransition animation];
transition.duration = animationDuration;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
transition.subtype = transitionType;
transition.delegate = [[TransitionDelegate alloc] initWithScreenshot:screenShot];
[window addSubview:destination.view];
[screenShot.window.layer addAnimation:transition forKey:nil];
*/
const BOOL animationsEnabled = [UIView areAnimationsEnabled];
[UIView setAnimationsEnabled:NO];
{
destination.view.frame = destinationFrame;
}
[UIView setAnimationsEnabled:animationsEnabled];
[UIView animateWithDuration:animationDuration
animations:^
{
destination.view.frame = originalFrame;
screenShot.frame = screenshotFrame;
}
completion:^(BOOL finished)
{
[screenShot removeFromSuperview];
}];
/*
const BOOL animationsEnabled = [UIView areAnimationsEnabled];
[UIView setAnimationsEnabled:NO];
{
CGRect frame = destination.view.frame;
frame.origin.x += source.view.bounds.size.width;
destination.view.frame = frame;
}
[UIView setAnimationsEnabled:animationsEnabled];
[UIView animateWithDuration:animationDuration
animations:^
{
destination.view.frame = originalFrame;
CGRect frame = screenShot.frame;
frame.origin.x -= screenShot.bounds.size.width;
screenShot.frame = frame;
}
completion:^(BOOL finished)
{
[screenShot removeFromSuperview];
}];
*/
/*
CATransition* transition = [CATransition animation];
transition.duration = animationDuration;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
switch ([UIApplication sharedApplication].statusBarOrientation)
{
case UIInterfaceOrientationPortraitUpsideDown:
transition.subtype = kCATransitionFromLeft;
break;
case UIInterfaceOrientationLandscapeLeft:
transition.subtype = kCATransitionFromBottom;
break;
case UIInterfaceOrientationLandscapeRight:
transition.subtype = kCATransitionFromTop;
break;
default:
transition.subtype = kCATransitionFromRight;
break;
}
[source.view.window.layer addAnimation:transition forKey:nil];
[source presentViewController:destination animated:NO completion:nil];
*/
}
static UIImageView* screenShotOfView(UIView* view)
{
// Create a snapshot for animation
UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
[view.layer renderInContext:context];
UIImageView* screenShot = [[UIImageView alloc] initWithImage:UIGraphicsGetImageFromCurrentImageContext()];
UIGraphicsEndImageContext();
return screenShot;
}
@end
/*
@implementation TransitionDelegate
{
UIView* screenshot;
}
- (id) initWithScreenshot: (UIView*) screenshot
{
if (self = [super init])
{
self->screenshot = screenshot;
}
return self;
}
- (void) animationDidStop: (CAAnimation*) theAnimation
finished: (BOOL) flag
{
[screenshot removeFromSuperview];
}
@end
*/