显示连续modalViews的正确方法

时间:2010-04-21 02:23:56

标签: iphone animated dismiss modalviewcontroller

我有两个视图需要以模态方式显示,一个接一个。如果我们连续解雇和显示,这不起作用,如下所示:

[rootController dismissModalViewControllerAnimated: YES];
[rootController presentModalViewController: psvc animated: YES];

第二个模态视图根本没有出现。

我见过这样的修复:

[rootController dismissModalViewControllerAnimated: YES];
[[UIApplication sharedApplication] beginIgnoringInteractionEvents];
[self performSelector: @selector(seekModal) withObject: nil afterDelay: 0.5];
[[UIApplication sharedApplication] endIgnoringInteractionEvents];

问题是这不会一直有效(所需的延迟有时是优越的。)

另一种可能的解决方法是消除动画:

[rootController dismissModalViewControllerAnimated: NO];
[rootController presentModalViewController: psvc animated: YES];

但是我真的很想保留动画,以保持第一个模态不受影响的感觉。有什么建议吗?

7 个答案:

答案 0 :(得分:17)

编辑:在iOS5 +中执行此操作的“正确”机制是使用– dismissViewControllerAnimated:completion:方法,并从完成块中显示顺序视图控制器。


以模态方式显示的viewcontroller将具有viewDidDisappear:animated:方法,一旦模态解除动画完成就会调用。 AFIK这是唯一可以挂钩来启动后续presentModalViewController的地方:动画:调用。

我有一个用于呈现模态视图控制器的类,它通过在解雇完成后回显到呈现视图控制器来实现您正在寻找的逻辑。要使用这个类,只需使用普通的presentViewController:animated:call来分配/初始化一个实例并呈现。在呈现视图控制器上实现以下方法:

- (void) modalViewControllerDidDismiss:(UIViewController *)modalViewController

这将在模态视图控制器消失时立即调用,此时您可以呈现新的模态视图控制器。

一件好事 - 由于这个类是UINavigationController的特化,你可以根据需要配置navigationBar的开/关。该类还有内置逻辑,可以根据需要显示一个关闭按钮。

这是类定义:

@protocol TSModalViewControllerDelegate

- (void) modalViewControllerDidDismiss: (UIViewController*) modalViewController;

@end

@interface TSModalViewController : UINavigationController 
{
    UIViewController*   _originalParentViewController;
}
@property BOOL dismissButtonHidden;

- (id) initWithViewController: (UIViewController*) vc;
- (id) initWithClass: (Class) c;
- (id) initWithClass: (Class) c nibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil;

@end

类实现:

@implementation TSModalViewController
@synthesize dismissButtonHidden;

- (id) initWithViewController: (UIViewController *)vc
{
    return [super initWithRootViewController: vc];
}

- (id) initWithClass:(Class)c
{
    UIViewController* vc = [[[c alloc] init] autorelease];
    return [self initWithViewController: vc];
}

- (id) initWithClass: (Class) c nibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    UIViewController* vc = [[[c alloc] initWithNibName:nibNameOrNil bundle:nibBundleOrNil] autorelease];
    return [self initWithViewController: vc];
}

- (void) viewDidAppear: (BOOL) animated
{
    [super viewDidAppear: animated];

    [_originalParentViewController release];
    _originalParentViewController = [self.parentViewController retain];

    if (!self.dismissButtonHidden)
    {
        UIBarButtonItem* dismissButton = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem: UIBarButtonSystemItemStop
                                                                                        target: self 
                                                                                        action: @selector(onDismiss:)] autorelease];

        UIViewController* rootViewController = [self.viewControllers objectAtIndex:0];

        rootViewController.navigationItem.leftBarButtonItem = dismissButton;
        self.navigationBarHidden = NO;
    }   
}

- (void) viewDidDisappear:(BOOL)animated
{
    [super viewDidDisappear: animated];
    if ( [_originalParentViewController respondsToSelector: @selector(modalViewControllerDidDismiss:)] )
    {
        [_originalParentViewController performSelector: @selector(modalViewControllerDidDismiss:) withObject: self];
    }
}

- (void) dismissModalViewControllerAnimated:(BOOL)animated
{
    return [self.parentViewController dismissModalViewControllerAnimated: animated];
}

- (void) onDismiss: (id) sender
{
    [self.parentViewController dismissModalViewControllerAnimated: YES];
}

- (void) didReceiveMemoryWarning 
{
    [super didReceiveMemoryWarning];
}

- (void) viewDidUnload 
{
    [super viewDidUnload];
}

- (void)dealloc 
{
    [_originalParentViewController release];
    [super dealloc];
}

@end

,以下是如何使用它(在一些普通视图控制器的上下文中):

- (void) onShowIt:(id)sender
{
    TSModalViewController* mvc = [[[TSModalViewController alloc] initWithClass: [MyModalViewController class] nibName: @"MyModalViewController" bundle:nil] autorelease];
    mvc.dismissButtonHidden = YES;  // set to no if you don't want an "automatic" close button

    [self presentModalViewController: mvc animated: YES];
}

,这是解雇回调方法,它提供了一个新的模态视图控制器:

- (void) modalViewControllerDidDismiss:(UIViewController *)modalViewController
{
    MyModalViewController* vc = [[[MyModalViewController alloc] initWithNibName: @"MyModalViewController" bundle:nil] autorelease];
    vc.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;

    TSModalViewController* mvc = [[[TSModalViewController alloc] initWithViewController: vc] autorelease];

    [self presentModalViewController: mvc animated: YES];
}

答案 1 :(得分:0)

rootController可以告诉它上面的最后一个模态视图控制器何时消失,因为它将收到一个viewDidAppear:。您是否尝试将后续视图控制器的presentModalViewController链接到那个?

答案 2 :(得分:0)

如果您真的想要将多个视图动画链接在一起,我实际上建议您自己处理动画逻辑。这不是太棘手,然后你可以对视图的呈现方式进行细粒度的控制。我刚刚在这里写了一些类似的东西:

iOS -- how do you control the size of a modal view controller?

您可以为视图设置动画,关闭视图动画,并在调用animationDidStop选择器时,为您的第二个视图设置动画。这个很好的部分是您还可以使用视图不透明度和动画方向,以及确定视图应该出现的确切时间。例如,当第一个视图滑开时,您可以让第二个视图在第一个视图上滑动;无需等待第一个完成动画。

答案 3 :(得分:0)

您的问题是否与“在模态视图中显示模态视图”有关? 我在这里发布了一个答案: iPhone modal view inside another modal view?

答案 4 :(得分:0)

我发现这样的最佳解决方案(如果它们都是父视图的相同子级)是将其视图修补到启用了分页的UIScrollView上(您可以在底部添加页面控件以使其清晰然后在导航时将控制器的视图添加到页面视图中,当它们离开屏幕时移除。

如果控制器依赖于此调用,您可能还需要虚拟调用-viewWillAppear和-viewWillDisappear。当你编写所有内容时,它感觉有点黑客,但是一旦你完成它的工作,它看起来平滑自然,没有任何等待动画一个视图,然后动画下一个视图走了。

答案 5 :(得分:0)

我发现使用模态视图的-viewDidDissapear可以非常好地调用呈现视图控制器上的方法。一个好处是能够延迟模态视图控制器上的重新分配。请发布我可以做的任何改进。 我创建这个协议的灵感来自iOS 5的“dismissViewControllerAnimated:completion:”,另外还有UIViewController。我想在iOS 4.3中使用此功能。


<强> PresentorDelegateProtocol.h

@protocol PresentorDelegateProtocol <NSObject>
@optional

/* 

Extra protocol methods defined in protocol for flexibility.  
Main methods are:
- (void)dismissPresentingModalViewController:(id)modalView animated:(BOOL)animated; 
- (void)modalViewDissapeared:(id)modalView;  //used in modal view's -viewDidDissapear

*/

- (void)dismissPresentingModalViewController:(id)modalView animated:(BOOL)animated;
- (void)modalViewDissapeared:(id)modalView; 

// use the block in this method send messages to save state, etc.  This is the one I like to use.
- (void)dismissPresentingModalViewController:(id)modalView animated:(BOOL)animated withBlock:(void(^)())block;

// use in other classes that are not controlling dismissal of the modal view
- (void)executeBlockOnModalDissapearance: (void(^)())block;

@end

<强> PresentingViewController.h

#import "PresentorDelegateProtocol.h"
@interface PresentingViewController : UIViewController <PresentorDelegateProtocol>
- (void)showModalVC;
@end

<强> ModalViewController.h

#import "PresentorDelegateProtocol.h"
@interface ModalViewController : UIViewController
@property (nonatomic, assign) id <PresentorDelegateProtocol> presentorDelegate;
- (void)close;
@end

<强> PresentingViewController.m

#import "PresentingViewController.h"
#import "ModalViewController.h"
@implementation PresentingModalViewController
- (void)showModalVC
{
    ModalViewController *modalVC = [[ModalViewController alloc] initWithNibName:@"ModalViewController" bundle:nil];
    modalVC.presentorDelegate = self;
    [self presentModalViewController:modalVC animated:YES];
}
- (void)dismissPresentingModalViewController:(id)modalView animated:(BOOL)animated
{
    if ([modalView isKindOfClass:[ModalViewController class]]) {
        NSLog(@"Can invoke based on class"); 
    }
    [self dismissModalViewControllerAnimated:animated];    
}
- (void)dismissPresentingModalViewController:(id)modalView animated:(BOOL)animated withBlock:(void(^)())block
{
    block();  
    /* execute block before or after calling to dismiss modal view */
    [self dismissPresentingModalViewController:modalView animated:animated];
    //block();
}
- (void)modalViewDissapeared:(id)modalView
{
    if ([modalView isKindOfClass:[ModalViewController class]]) {
        NSLog(@"Do stuff based on class.");
    }
}
- (void)executeBlockOnModalDissapearance: (void(^)())block
{
    block();
    NSLog(@"This delay's dealloc on modal view until block completes");
}
@end

<强> ModalViewController.m

#import "ModalViewController.h"
@implementation ModalViewController
@synthesize presentorDelegate;

- (void)close
{
    if (1 == 0 /*need to do something before dealloc*/){
        [self.presentorDelegate dismissPresentingModalViewController:self animated:YES withBlock:^{
            NSLog(@"Do stuff with block.  Save, animate, etc");
        }];

    } else {
        [self.presentorDelegate dismissPresentingModalViewController:self animated:YES];
    }
}

- (void)viewDidDisappear:(BOOL)animated
{
    if (1 == 0 /*stuff to do*/){
        [self.presentorDelegate executeBlockOnModalDissapearance:^{
        // do stuff before modal view is deallocated
        }];
    }
    [self.presentorDelegate modalViewDissapeared:self];

    presentorDelegate = nil;
    [super viewDidDisappear:animated];
}
@end;

答案 6 :(得分:0)

// present modal view inside another presented modal view

    FirstViewController *firstVC = [[FirstViewController alloc] initWithNibName:@"FirstViewController" bundle:nil];
    UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController: firstVC];

    // Note: you can use your viewcontroller instead self.window.rootViewController

    [self.window.rootViewController presentViewController:navController animated:YES completion:^{
                //code...
                    SecondViewController *secondVC = [[SecondViewController alloc] initWithNibName:@"SecondViewController" bundle:nil];

                    [navController presentViewController: secondVC animated:YES completion:nil];

                }
            }];