具有缩放动画的 pushViewController,显示背景

Posted

技术标签:

【中文标题】具有缩放动画的 pushViewController,显示背景【英文标题】:pushViewController with scaling animation, that shows background 【发布时间】:2011-04-29 15:23:36 【问题描述】:

我找到了使用缩放动画调用 pushViewController 的方法, 像 facebook iPhone 应用程序主菜单图标点击动画。 (新的 viewController 从中心弹出,并缩放到原始大小。)

我搜索了几种方法来改变 pushViewController 的动画。

首先,我尝试了这个:

viewController.view.transform = CGAffineTransformMakeScale(0.5f, 0.5f);
[UIView animateWithDuration:0.5f animations:^ 
    viewController.view.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
];
[self.navigationController pushViewController:viewController animated:NO];

但是有问题, 动画开始时旧的 viewController 消失了,只有白色背景。

如果我使用 CATransition 来改变动画, 我可以同时显示新旧视图控制器, 但没有缩放动画,只有移动、推入、淡入动画。

我想同时显示新旧视图控制器,例如 CATransition 动画, 并且需要实现我的自定义动画的方法。

这是我的最后一个建议,肮脏的方式:

[self.view addSubview:viewController.view];
viewController.view.transform = CGAffineTransformMakeScale(0.5f, 0.5f);
[UIView animateWithDuration:0.5f
                      delay:0.0f
                    options:UIViewAnimationCurveEaseInOut
                 animations:^
    viewController.view.alpha = 1.0f;
    viewController.view.transform = CGAffineTransformMakeScale(1.0f, 1.0f); 
 completion:^(BOOL finished) 
    [self.navigationController pushViewController:viewController animated:NO];
    [viewController release];
];

首先,我添加了新的 viewController.view 作为子视图,我可以同时显示带有新旧视图的动画。 动画结束后,pushViewController。

这样我就可以实现我的想法, 但我认为这是肮脏的方式,并且仍然存在问题:

导航栏项目不会立即改变,它会在动画结束后改变。

有没有简单明了的方法来实现这个动画? 我认为不改变pushViewController的原始实现是不可能的,我应该继承pushViewController吗?

感谢阅读,感谢您的帮助。

【问题讨论】:

【参考方案1】:

在回答原始问题时,我不确定您是否认为这是一种改进,但我认为您可以在 IB 中正在转换 (a) 的视图中包含导航栏,添加一个navigationBar 到要转换到的视图,(b)在开始动画之前为根导航控制器导航栏的隐藏设置动画(因此当新视图滑入时它会滑落),以及(c)在您的完成块中动画,隐藏新视图添加的导航栏并取消隐藏根导航控制器的导航栏。也许这会使某些东西更接近您的意图,尽管可能与您最初设想的不同。不过,它并不完美。代码可能类似于:

MyViewController *newController = [[MyViewController alloc] initWithNibName:@"MyView" bundle:nil];
newController.view.transform = CGAffineTransformMakeScale(0.1f, 0.1f);
[self.navigationController setNavigationBarHidden:YES animated:YES];
[self.view addSubview:newController.view];

[UIView animateWithDuration:0.5f
                      delay:0.0f
                    options:UIViewAnimationCurveEaseInOut
                 animations:^
                     newController.view.alpha = 1.0f;
                     newController.view.transform = CGAffineTransformMakeScale(1.0f, 1.0f); 
                  completion:^(BOOL finished) 
                     [newController.view removeFromSuperview];
                     [self.navigationController pushViewController:newController animated:NO];
                     [newController.tempBar setHidden:YES];
                     [self.navigationController setNavigationBarHidden:NO animated:NO];
                 ];

它为您的“肮脏方式”添加了更多“污垢”,但它可能会呈现您正在寻找的效果。您只想使新视图中的临时导航栏看起来与最终的根导航控制器的导航栏看起来一样,并且用户不应该更聪明。如果这仍然不能完全达到您想要的效果,您可以完全隐藏根导航控制器的栏,并始终将您的导航放在您的视图上,在这种情况下,当您过渡到您的视图时,效果将完全无缝新视图。

或者,您可以保留现有代码,并在转换之前设置当前视图的标题,这样看起来标题更改可能是预先发生的。但是,您可能必须在返回时重新设置它,设置返回按钮等,所以它可能不值得。

顺便说一句,这与原始问题有点相切(所以我提前道歉),但是为了回应另一个建议,您不应该为视图控制器设置动画,而应该只为视图设置动画,我想发出警告。是的,从技术上讲这是对的,但我很担心这是否会鼓励人们采用通过(a)创建新视图控制器来添加新视图的不良做法; (b) 将该视图控制器的视图添加为当前视图的子视图;但是 (c) 不使用新的视图控制器将其添加到您的视图控制器层次结构中(例如 pushViewController 或 presentViewController)。

如果这就是另一个答案的意图,我不得不说我认为这是一个非常糟糕的想法。 (如果这不是本意,那么我很抱歉,但我认为人们很容易误解这个建议。)最重要的是,我最近看到人们这样做:

MyViewController *newController = [[MyViewController alloc] initWithNibName:@"MyView" bundle:nil];

// do some animation around adding the new view as a subview of your current view 
// but neglect to ever invoke pushViewController or presentViewController

[self.view addSubview:newController.view];

推荐这有几个原因。首先,如果它是一个 ARC 项目,当 newController 超出范围时,您的视图控制器将被丢弃。 (似乎在 SO 上发布了大量此类问题,这是由于人们过渡到 ARC 时引发的异常。)当然,您可以通过将 newController 设为主控制器的 ivar 来解决此问题,这样它就不会脱落范围,但要小心记住在主控制器最终被释放或删除子视图时将其设置为 nil,否则可能会泄漏。而且如果你是非ARC项目,更容易泄露,所以一定要在子视图被移除的时候释放视图控制器,防止泄露。

其次,可能更重要的是,虽然上面的代码似乎可以工作(或者至少如果您将控制器设置为 ivar),但最终您的视图控制器的层次结构与视图的层次结构之间存在脱节。 (在session 102 of WWDC 2011 中,关于另一个主题,视图控制器包含,它包括一个冗长但相关的讨论,讨论保持视图控制器层次结构和视图层次结构协调的重要性,例如,旋转事件可能无法正确发送到新视图的控制器,因为控制器不在控制器层次结构中,因此 ios 不知道将它们发送到您的控制器。)底线,如果您使用另一个视图控制器的视图添加为当前视图的子视图,它很脆弱,容易受到影响中断 iOS 升级,并且诸如旋转事件之类的事情不会正确地传递给您的新视图的视图控制器。如果您这样做,至少要意识到这不是好的做法,并自行评估是否要这样做。没有必要绕过正确的视图控制器层次结构,我个人想劝阻那些倾向于这样做的人。

底线,如果您要转换到不同视图控制器的视图,您真的想坚持使用 pushViewController 或 presentViewController,并围绕新控制器的视图制作动画(通常它远没有这个动画那么复杂...通常它非常简单,我已经更轻松地完成了各种淡入淡出和翻转动画......正是使用变换使这变得复杂)。

替代方案,如果您想为新的 UIView 子视图设置动画而不处理视图控制器,只需确保它不是另一个视图控制器的视图,而是,例如,您以编程方式创建的使用相同视图控制器的视图作为您的原始视图。如果您决定使用另一个视图控制器的视图作为现有控制器的子视图,只需知道它有点脆弱,并且某些事件可能不会像您期望的那样传输到您的新视图控制器,并且可能不会像您可能想要的“未来证明”。

【讨论】:

【参考方案2】:

我不认为动画是用视图控制器完成的。您可以只保留现有的视图控制器。创建一个应该放大的新 UIView。将该新 UIView 添加到现有视图控制器并对其应用动画。

【讨论】:

以上是关于具有缩放动画的 pushViewController,显示背景的主要内容,如果未能解决你的问题,请参考以下文章

CGAffineTransform 缩放和平移 - 动画前跳转

具有缩放功能的图表

使用渐变缩放 UIView

动画1

动画边界更改时具有 CALayer 有线效果的自定义视图

动画边界更改时具有 CALayer 有线效果的自定义视图