我有一个内容UIViewController,它使用自定义转换呈现设置UIViewController。演示文稿位于presentViewController:animated:completion:
。
当我稍后使用dismissViewControllerAnimated:completion:
关闭设置时,演示控制器会突然跳回设置控制器演示之前的初始位置。
我在设备上有解决方法但不是模拟器。但是,我想知道我做错了什么,而不是黑客入侵让它消失的小屋。我还打算让这个动画互动,我怀疑这个问题会在我这样做时放大。
理想的效果是呈现控制器在屏幕上向下滑动,并且呈现的控制器被看作位于其后面,从它抬起以填满屏幕。在呈现控制器的使用寿命期间,呈现控制器的顶部保持在屏幕上。它停留在屏幕的底部,但高于显示的控制器。
您可以想象在汽车(前部控制器)上抬起引擎盖以查看后面的发动机(显示的设置),但是发动机罩在底部保持可见状态。
我计划对此进行改进,以便呈现控制器看起来真的以3d方式提升视角,但我还没有那么远。
当设置被取消时,原始显示控制器(发动机罩)应向上滑回屏幕,并且呈现的控制器(设置)稍微下沉(关闭发动机罩)。
这里是切换屏幕上和屏幕外设置的方法(它只是由UIButton调用)。您会注意到呈现视图控制器将自己设置为<UIViewControllerTransitioningDelegate>
。
-(void) toggleSettingsViewController
{
const BOOL settingsAreShowing = [self presentedViewController] != nil;
if(!settingsAreShowing)
{
UIViewController *const settingsController = [[self storyboard] instantiateViewControllerWithIdentifier: @"STSettingsViewController"];
[settingsController setTransitioningDelegate: self];
[settingsController setModalPresentationStyle: UIModalPresentationCustom];
[self presentViewController: settingsController animated: YES completion: nil];
}
else
{
[self dismissViewControllerAnimated: YES completion: nil];
}
}
要实现<UIViewControllerAnimatedTransitioning>
,呈现视图控制器也会将其自身返回为<UIViewControllerAnimatedTransitioning>
-(id<UIViewControllerAnimatedTransitioning>) animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
return self;
}
-(id<UIViewControllerAnimatedTransitioning>) animationControllerForDismissedController:(UIViewController *)dismissed
{
// Test Point 1.
return self;
}
最后,呈现视图控制器将收到animateTransition:
:
-(void) animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIViewController *const fromController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *const toController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
const BOOL isUnwinding = [toController presentedViewController] == fromController;
const BOOL isPresenting = !isUnwinding;
UIViewController * presentingController = isPresenting ? fromController : toController;
UIViewController * presentedController = isPresenting ? toController : fromController;
if(isPresenting)
{
// Add the presented controller (settings) to the view hierarchy _behind_ the presenting controller.
[[transitionContext containerView] insertSubview: [presentedController view] belowSubview: [presentingController view]];
// Set up the initial position of the presented settings controller. Scale it down so it seems in the distance. Alpha it down so it is dark and shadowed.
presentedController.view.transform = CGAffineTransformMakeScale(0.9, 0.9);
presentedController.view.alpha = 0.7;
[UIView animateWithDuration: [self transitionDuration: transitionContext] animations:^{
// Lift up the presented controller.
presentedController.view.transform = CGAffineTransformMakeScale(1.0, 1.0);
// Brighten the presented controller (out of shadow).
presentedController.view.alpha = 1;
// Push the presenting controller down the screen – 3d effect to be added later.
presentingController.view.layer.transform = CATransform3DMakeTranslation(0,400,0);
} completion: ^(BOOL finished){
[transitionContext completeTransition: ![transitionContext transitionWasCancelled]];
}];
}
else
{
// Test Point 2.
// !!!This line should not be needed!!!
// It resets the presenting controller to where it ought to be anyway.
presentingController.view.layer.transform = CATransform3DMakeTranslation(0,400,0);
[UIView animateWithDuration: [self transitionDuration: transitionContext] animations:^{
// Bring the presenting controller back to its original position.
presentingController.view.layer.transform = CATransform3DIdentity;
// Lower the presented controller again and put it back in to shade.
presentedController.view.transform = CGAffineTransformMakeScale(0.9, 0.9);
presentedController.view.alpha = 0.4;
} completion:^(BOOL finished) {
[transitionContext completeTransition: ![transitionContext transitionWasCancelled]];
}];
}
}
-(NSTimeInterval) transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
return 0.5;
}
在上面的代码中,我已经指出 !!!不应该需要此行!!! 。
在测试点1 和测试点2 之间发生的情况是,呈现视图控制器的屏幕位置被重置为默认的全屏界限。因此,它不是在屏幕的底部准备好再次平滑地重新启动动画,而是突然跳到屏幕上以确定它是否也能平滑地制作动画!
我尝试了各种方法在屏幕上动画显示视图控制器:
在所有情况下,在测试点1 ,当要求转换委托时,呈现控制器按照我的预期设置。但是,在所有情况下,在测试点2 时,呈现视图控制器已丢失正确的位置并已被清除&#34;拥有我想要动画的正常全屏位置。
在上面的工作中,我明确地将呈现视图控制器重新定位到动画开始处的位置 !!!不应该需要这一行!!! 。这似乎适用于具有当前版本iOS 7的设备。但是,在模拟器上,控制器在清除位置可见至少一帧。
我怀疑我做错了什么,而且我会在解决另一个问题时遇到麻烦。
任何想法是怎么回事?谢谢!
答案 0 :(得分:38)
使用自定义过渡动画解雇模态呈现的视图控制器的一些潜在问题:
鉴于这一切,我认为这应该有效:
-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIViewController *fromController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *containerView = transitionContext.containerView;
const BOOL isUnwinding = [toController presentedViewController] == fromController;
const BOOL isPresenting = !isUnwinding;
UIViewController *presentingController = isPresenting ? fromController : toController;
UIViewController *presentedController = isPresenting ? toController : fromController;
[containerView addSubview:presentingController.view];
[containerView bringSubviewToFront:presentingController.view];
if(isPresenting)
{
// Set up the initial position of the presented settings controller. Scale it down so it seems in the distance. Alpha it down so it is dark and shadowed.
presentedController.view.transform = CGAffineTransformMakeScale(0.9, 0.9);
presentedController.view.alpha = 0.7;
[UIView animateWithDuration: [self transitionDuration: transitionContext] animations:^{
// Lift up the presented controller.
presentedController.view.transform = CGAffineTransformMakeScale(1.0, 1.0);
// Brighten the presented controller (out of shadow).
presentedController.view.alpha = 1;
// Push the presenting controller down the screen – 3d effect to be added later.
presentingController.view.layer.transform = CATransform3DMakeTranslation(0,400,0);
} completion: ^(BOOL finished){
[transitionContext completeTransition: ![transitionContext transitionWasCancelled]];
}];
}
else
{
presentedController.view.transform = CGAffineTransformMakeScale(0.9, 0.9);
presentedController.view.alpha = 0.7;
[UIView animateWithDuration: [self transitionDuration: transitionContext] animations:^{
// Bring the presenting controller back to its original position.
presentingController.view.layer.transform = CATransform3DIdentity;
// Lower the presented controller again and put it back in to shade.
presentedController.view.transform = CGAffineTransformMakeScale(0.9, 0.9);
presentedController.view.alpha = 0.4;
} completion:^(BOOL finished) {
[transitionContext completeTransition: ![transitionContext transitionWasCancelled]];
}];
}
}
答案 1 :(得分:0)
最初,我考虑在CATransition
和presentViewController:animated:completion:
视图控制器时使用dismissViewControllerAnimated:completion:
来获得自定义转换效果。但是,当您显示设置视图控制器时,您希望显示View Controller的一部分,然后我认为CATransition
无效,因为您无法完全控制您想要移动视图控制器的距离。
我认为最简单的方法是使用一个带有两个全屏UIView的View Controller。对于第一个UIView(View Controller的视图,即self.view),您可以布局设置,而在第二个UIView上,它是常规视图。在ViewDidLoad中,使用[self.view addSubview:2ndView];
添加第二个视图。稍后当您想要显示设置视图时,可以执行
CGRect frame = secondView.frame;
frame.origin.y = the_y_coordinate_you_like;
UIView animateWithDuration:0.2 animations:^{
secondView.frame = frame;
}];
然后用另一种方式将第二视图带回来。