2010-04-21 13 views
9

Tengo dos vistas que deben mostrarse modalmente, una después de la otra. Esto no funciona si descartamos y mostramos consecutivamente, así:Forma correcta de mostrar vistas modales consecutivas

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

La segunda vista modal simplemente no aparece.

que he visto una corrección que fue algo como esto:

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

El problema es que esto no va a funcionar todo el tiempo (el retardo necesario es superior, a veces).

Otra posible solución sería eliminar la animación:

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

Pero realmente me gustaría mantener la animación, para mantener la sensación de que el primer modal está fuera del camino. ¿Alguna sugerencia?

+3

Por qué no utilizar el modal controlador de vista que cambia su punto de vista? Dos controladores de vista modal seguidos serían un poco molestos. – bpapa

+0

Si son "consecutivos", considere usar la navegación. –

+0

¿Estás 100% seguro de que el descarte de la 1ª vista modal y la apertura de la 2ª se realizan en el contexto del hilo principal? – yonel

Respuesta

17

EDIT: El mecanismo "correcto" para hacer esto en iOS5 + sería utilizar el método – dismissViewControllerAnimated:completion:, y presentar el controlador de vista secuencial desde el bloque de finalización.


El ViewController que está siendo mostrado de forma modal tendrá su viewDidDisappear: animada: método llama una vez que el modal-despido-animación es completa.AFIK este es el único lugar donde puedes enlazar para iniciar un presentModalViewController siguiente: animado: llamada.

Tengo una clase que utilizo para presentar controladores de vistas modales e implementa la lógica que está buscando mediante una devolución de llamada al controlador de vista de presentación una vez que se completa el despido. Para usar esta clase, simplemente alloc/init una instancia y presente usando la función presenteViewController: animated: call. Aplicar el siguiente método en el controlador de vista de presentación:

- (void) modalViewControllerDidDismiss:(UIViewController *)modalViewController 

Esto se llama a la vez el controlador de vista modal se ha ido, y se puede presentar un nuevo controlador de vista modal en este momento.

Una cosa buena también, ya que esta clase es una especialización de UINavigationController, puede configurar la barra de navegación como se desee. La clase también tiene una lógica incorporada para mostrar un botón de descartar, como quieras.

Aquí está la definición de clase:

@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 

Y la implementación de la clase:

@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 

y, aquí es cómo se puede usar (en el contexto de algún controlador de vista normal):

- (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]; 
} 

y, aquí está el método de devolución de llamada de salida, que presenta un nuevo controlador de vista modal:

- (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]; 
} 
0

rootController puede decir cuándo ha desaparecido el último de los controladores de vista modal en la parte superior, ya que recibirá un viewDidAppear :. ¿Has intentado vincular el presente ControladorModalView: del controlador de vista posterior a eso?

+0

Todavía se encuentra con problemas de temporización ... Se llama a viewDidAppear antes de que la vista realmente haya aparecido, al parecer. –

+0

Siempre hay 'performSelector: foo afterDelay: 0' que podría funcionar en viewDidAppear ... –

0

Si realmente desea encadenar varias animaciones de vista juntas, sugeriría simplemente que maneje la lógica de animación usted mismo. No es demasiado complicado, y luego puede tener un control detallado sobre cómo se presentan las vistas. Me acabo de escribir algo similar para la otra pregunta aquí:

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

Sólo puede animar la vista sobre, animar la vista fuera, y cuando el selector de animationDidStop se llama, animado su segundo punto de vista sobre. Lo bueno de esto es que también puedes jugar con la opacidad de la vista y la dirección de la animación, así como decidir exactamente cuándo deben aparecer las vistas. Por ejemplo, podría hacer que la segunda vista se deslice sobre la primera vista a medida que la primera vista se aleja; no es necesario esperar al primero para completar su animación.

+0

Idea interesante. Parece que esto te obliga a salir de MVC, ¿no? Al igual que terminarás con dos vistas y un controlador. –

+0

MVC no significa necesariamente una de cada; en realidad se trata de separar esos componentes entre sí y dejar que cada uno haga su parte. En realidad, es bastante común que un controlador de vista administre múltiples vistas. Después de todo, muchos/la mayoría de los componentes de la interfaz de usuario que agrega a la vista de un controlador de vista (botones, imágenes, etc.) y animaciones son solo vistas. Es mejor pensar que cada controlador de visualización representa un fragmento de pantalla completa de su programa y el cerebro detrás de esa sección de la aplicación. El controlador es la lógica que conecta los modelos y vistas juntos. – atticus

0

¿Su problema está relacionado con "mostrar una vista modal dentro de una vista modal"? he puesto una respuesta acerca de esto aquí: iPhone modal view inside another modal view?

+0

No realmente. Se trata de cerrar una vista modal y abrir otra. iOS parece bloquearse cuando cierras y presentas una vista demasiado cercana. Esto es particularmente un problema con el selector de cámara, ya que tiene un gran tiempo de carga/descarga. Si desea descartar el selector de cámara y mostrar algo más, puede toparse con todo tipo de comportamiento extraño. –

0

La mejor solución que encontré para algo como esto (si es que son todos los niños iguales de la vista padre) es parchear sus puntos de vista sobre una UIScrollView con paginación está habilitada, (puede agregar un control de página en la parte inferior para que quede nítido y para la navegación) luego agregue las vistas de los controladores a la vista de página a medida que aparecen en pantalla, eliminándolas a medida que pasan fuera de la pantalla.

También puede que tengas que hacer una llamada ficticia -viewWillAppear y -viewWillDisappear si los controladores confían en que se invocó. Se siente como un hack-ish cuando lo estás codificando todo, pero una vez que lo tienes funcionando parece liso y natural, y no hay nada de la espera asociada con la animación de una vista, luego animando la siguiente una vez se ha ido.

0

puedo encontrar utilizando -viewDidDissapear de la vista modal para invocar métodos en la vista de presentación del controlador de trabajo muy bien. Un beneficio es la capacidad de retrasar la desasignación en el controlador de vista modal. Por favor, publique cualquier mejora que pueda hacer. Mi inspiración para crear este protocolo vino de iOS 5: "dismissViewControllerAnimated: completion:" además de UIViewController. Quería esta funcionalidad en 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; 
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]; 

       } 
      }]; 
Cuestiones relacionadas