非全屏和全屏 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) 创建一个新视图,以全屏显示您刚刚拍摄的图像(使用UIImageinitWithFrame) 隐藏状态栏 展示模态视图控制器

然后要关闭模态视图,只需反转步骤即可。

编辑

因为您无法截取状态栏的屏幕截图,所以无法解决此问题。

【讨论】:

不能对状态栏进行截图。 UIGetScreenImage() 曾经这样做,但你不能再这样做了。状态栏是它自己的窗口,你不能得到它的引用,afaik。【参考方案5】:

它可以像使用 performSelector:withDelay 延迟 modalViewController 的呈现一样简单:

告诉状态栏动画出来,然后以正确的延迟启动模态控制器,使其与状态栏动画重合。

【讨论】:

以上是关于非全屏和全屏 UIViewController 之间的漂亮幻灯片过渡的主要内容,如果未能解决你的问题,请参考以下文章

画中画和全屏 - API 只能由用户手势启动

怎么QT窗口化,点游戏设置全屏。点不了

js之切换全屏和退出全屏实现

远程桌面连接_全屏问题

java 动态切换全屏,非全屏

Android Ireader的全屏与非全屏的切换效果实现