非全屏和全屏 UIViewController 之间的漂亮幻灯片过渡
Posted
技术标签:
【中文标题】非全屏和全屏 UIViewController 之间的漂亮幻灯片过渡【英文标题】:Nice slide transition between non-fullscreen and fullscreen UIViewController 【发布时间】:2011-12-11 12:45:37 【问题描述】:我有一个不是全屏的视图控制器(有一个状态栏),并且想要呈现一个全屏的模态视图控制器。
如果我在动画开始时隐藏状态栏(父级的 viewWillDisappear 或模态的 viewWillAppear),那么一会儿父级将在没有状态栏的情况下可见,看起来像一个错误。
如果我在动画结束时执行此操作(父级的 viewDidDisappear 或模态的 viewDidAppear),那么状态栏将在模态视图上显示片刻,即它不会显示为模态视图“覆盖它”。
有没有办法很好地做到这一点?
编辑:
一种可能性是至少在动画期间创建一个具有 windowLevel=alert 的 UIWindow。示例 iAd 广告似乎很好地覆盖了状态栏,没有另一个窗口,所以它一定是可能的。
【问题讨论】:
我认为您的 UIWindow 解决方案可能是正确的方法。当我们需要在视图控制器之间进行不寻常的转换时,我们会在我们的应用中使用类似的技术。 【参考方案1】:另一个有趣的小项目。这是我能想到的最好的。如果您不介意使用自己的容器控制器来管理呈现/关闭视图控制器,那还不错。我尝试以一般方式做事,但如果需要,可以将其整合到带有 ContainerViewController 的应用程序中。
请注意,我只实现了 UIModalTransitionStyleCoverVertical
的等效项。您也可以根据自己的喜好自定义动画。
相关动画代码:
- (void)presentViewController:(UIViewController *)viewControllerToPresent
// do nothing if no controller
if (!viewControllerToPresent) return;
[__viewControllers addObject:viewControllerToPresent];
CGRect toFrame = viewControllerToPresent.view.frame;
toFrame.origin = CGPointMake(0, CGRectGetMaxY(self.view.bounds));
viewControllerToPresent.view.frame = toFrame;
[UIView transitionWithView:self.view
duration:0.2
options:UIViewAnimationOptionTransitionNone
animations:^
[[UIApplication sharedApplication] setStatusBarHidden:viewControllerToPresent.wantsFullScreenLayout withAnimation:UIStatusBarAnimationSlide];
[self.view addSubview:viewControllerToPresent.view];
viewControllerToPresent.view.frame = [UIScreen mainScreen].applicationFrame;
completion:nil];
- (void)dismissViewController
// nothing to dismiss if showing first controller
if (__viewControllers.count <= 1) return;
UIViewController *currentViewController = [__viewControllers lastObject];
UIViewController *previousViewController = [__viewControllers objectAtIndex:__viewControllers.count - 2];
[UIView transitionWithView:self.view
duration:0.2
options:UIViewAnimationOptionTransitionNone
animations:^
[[UIApplication sharedApplication] setStatusBarHidden:previousViewController.wantsFullScreenLayout withAnimation:UIStatusBarAnimationSlide];
CGRect toFrame = currentViewController.view.frame;
toFrame.origin = CGPointMake(0, CGRectGetMaxY(self.view.bounds));
currentViewController.view.frame = toFrame;
completion:^(BOOL finished)
[currentViewController.view removeFromSuperview];
[__viewControllers removeLastObject];
];
【讨论】:
【参考方案2】:我使用以下代码在我的应用中执行此操作:
[[UIApplication sharedApplication] setStatusBarStyle: UIStatusBarStyleBlackOpaque];
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation: UIStatusBarAnimationSlide ];
DocumentListViewController * dl = [[DocumentListViewController alloc] initWithNibName:@"DocumentListView" bundle:nil] ;
UINavigationController * nav = [[UINavigationController alloc] initWithRootViewController:dl];
[dl release];
// Go to the list of documents...
[[self.view superview] addSubview:nav.view];
nav.view.alpha = 0.0 ;
[self hideActivityAlert];
[UIView animateWithDuration:1.0 animations:^
nav.view.alpha = 1.0; completion:^(BOOL A)
[self.view removeFromSuperview];
[self release]; ];
状态栏会在动画发生时快速呈现。
您必须确保在隐藏状态栏时第一个视图会填满空间。使用具有适当值的属性 autoresizingMask。
【讨论】:
Gabriel,但如果我理解正确的话,这是一个淡入淡出,而不是幻灯片动画。也许我应该更具体。 好的,现在我明白你想要什么了。您希望模态视图从底部显示标准动画并覆盖状态栏。但我认为这是不可能的,因为状态栏是用于应用程序而不是用于视图。因此,您最多可以做的是,就像在我的代码中一样,在同一动画中隐藏状态栏。然后,当模态视图出现时,该栏会隐藏动画。【参考方案3】:这是一个似乎可行的解决方案。你可以从我的 TSFullScreenModalViewController 派生出你想要模态呈现的视图控制器,或者你可以直接在视图控制器本身中实现代码。
@interface TSFullScreenModalViewController : UIViewController
UIWindow* _window;
- (void) presentFullScreenModal;
@end
@implementation TSFullScreenModalViewController
- (void) viewDidDisappear:(BOOL)animated
[super viewDidDisappear: YES];
[_window resignKeyWindow];
[_window release];
_window = nil;
- (void) presentFullScreenModal
UIViewController* rvc = [[UIViewController new] autorelease];
rvc.view.backgroundColor = [UIColor clearColor];
_window = [[UIWindow alloc] initWithFrame: [UIScreen mainScreen].bounds] ;
_window.windowLevel = UIWindowLevelStatusBar+1;
_window.backgroundColor = [UIColor clearColor];
_window.rootViewController = rvc;
[_window makeKeyAndVisible];
[UIApplication sharedApplication].statusBarHidden = YES;
[rvc presentModalViewController: self animated: YES];
[UIApplication sharedApplication].statusBarHidden = NO;
@end
派生您的模态视图控制器,如下所示:
@interface MyModalViewController : TSFullScreenModalViewController
- (IBAction) onDismiss:(id)sender;
@end
从另一个视图控制器使用它,像这样:
- (IBAction) onShowModal:(id)sender
MyModalViewController* mmvc = [[MyModalViewController new] autorelease];
[mmvc presentFullScreenModal];
最后,像往常一样关闭视图控制器:
- (IBAction) onDismiss:(id)sender
[self dismissModalViewControllerAnimated: YES];
【讨论】:
我已经在我的问题中写过关于 UIWindow 方法的问题,但我不想使用它,因为在状态栏或警报级别设置窗口还有很多其他问题。我已经检查过 iAd 没有使用其他窗口。 因此您的新要求是不要使用位于状态栏上方的窗口。在我花时间找出解决方案之前知道这一点会很高兴。 汤姆,我在您回答前 2 天添加了该内容。【参考方案4】:可能有点小题大做,但您考虑过吗:
-
以编程方式截取带有状态栏的第一个视图的屏幕截图(参见SO question)
创建一个新视图,以全屏显示您刚刚拍摄的图像(使用
UIImage
的initWithFrame
)
隐藏状态栏
展示模态视图控制器
然后要关闭模态视图,只需反转步骤即可。
编辑:
因为您无法截取状态栏的屏幕截图,所以无法解决此问题。
【讨论】:
不能对状态栏进行截图。 UIGetScreenImage() 曾经这样做,但你不能再这样做了。状态栏是它自己的窗口,你不能得到它的引用,afaik。【参考方案5】:它可以像使用 performSelector:withDelay 延迟 modalViewController 的呈现一样简单:
告诉状态栏动画出来,然后以正确的延迟启动模态控制器,使其与状态栏动画重合。
【讨论】:
以上是关于非全屏和全屏 UIViewController 之间的漂亮幻灯片过渡的主要内容,如果未能解决你的问题,请参考以下文章