Intercambiar vistas secundarias en una vista de contenedor


Sea ContainerView la vista del contenedor principal con dos vistas de contenido secundario: NavigationView y ContentView.

Ejemplo de Diseño de Vista

Me gustaría poder intercambiar el controlador de ContentView con otra vista. Por ejemplo, intercambiar un controlador de página de inicio con un controlador de página de noticias. Actualmente, la única forma en que puedo pensar para hacer esto es usando un delegado para decirle a ContainerView que quiero cambiar de vista. Esto parece una manera descuidada de hacer esto porque el ContainerViewController terminaría teniendo un montón de especial delegados para todas las subviews.

Esto también necesita comunicarse con el NavigationView que tiene información sobre qué vista se encuentra actualmente en el ContentView. Por ejemplo: si el usuario está en la página de noticias, la barra de navegación dentro de la vista de navegación mostrará que el botón de noticias está seleccionado actualmente.

Pregunta A: ¿Hay alguna forma de intercambiar el controlador en ContentView sin que un método delegado llame al propio ContainerView? Me gustaría hacer esto programáticamente (no storyboard).

Pregunta B: ¿Cómo puedo intercambiar controladores en ContentView desde NavigationView sin una llamada de delegado? Me gustaría hacer esto programáticamente (sin guion gráfico).

Author: gbk, 2013-10-03

3 answers

Cuando tiene vistas secundarias que tienen sus propios controladores de vista, debe seguir el patrón de controlador de contenedor personalizado. Consulte Creación de Controladores de Vista de Contenedores personalizados para obtener más información.

Asumiendo que ha seguido el patrón de contenedor personalizado, cuando desea cambiar el controlador de vista hijo (y su vista asociada) por la "vista de contenido", lo haría programáticamente con algo como:

UIViewController *newController = ... // instantiate new controller however you want
UIViewController *oldController = ... // grab the existing controller for the current "content view"; perhaps you maintain this in your own ivar; perhaps you just look this up in self.childViewControllers

newController.view.frame = oldController.view.frame;

[oldController willMoveToParentViewController:nil];
[self addChildViewController:newController];         // incidentally, this does the `willMoveToParentViewController` for the new controller for you

[self transitionFromViewController:oldController
                  toViewController:newController
                          duration:0.5
                           options:UIViewAnimationOptionTransitionCrossDissolve
                        animations:^{
                            // no further animations required
                        }
                        completion:^(BOOL finished) {
                            [oldController removeFromParentViewController]; // incidentally, this does the `didMoveToParentViewController` for the old controller for you
                            [newController didMoveToParentViewController:self];
                        }];

Cuando lo haces de esta manera, no hay necesidad para cualquier interfaz de protocolo delegado con el controlador de la vista de contenido (que no sea lo que iOS ya proporciona con los métodos Administrar Controladores de vista Secundarios en un contenedor personalizado).


Por cierto, esto asume que el controlador hijo inicial asociado con esa vista de contenido se agregó de la siguiente manera:

UIViewController *childController = ... // instantiate the content view's controller any way you want
[self addChildViewController:childController];
childController.view.frame = ... // set the frame any way you want
[self.view addSubview:childController.view];
[childController didMoveToParentViewController:self];

Si desea que un controlador hijo le diga al padre que cambie el controlador asociado con la vista de contenido, debe would:

  1. Defina un protocolo para esto:

    @protocol ContainerParent <NSObject>
    
    - (void)changeContentTo:(UIViewController *)controller;
    
    @end
    
  2. Defina el controlador padre para ajustarse a este protocolo, por ejemplo:

    #import <UIKit/UIKit.h>
    #import "ContainerParent.h"
    
    @interface ViewController : UIViewController <ContainerParent>
    
    @end
    
  3. Implementa el método changeContentTo en el controlador padre (como se ha descrito anteriormente):

    - (void)changeContentTo:(UIViewController *)controller
    {
        UIViewController *newController = controller;
        UIViewController *oldController = ... // grab reference of current child from `self.childViewControllers or from some property where you stored it
    
        newController.view.frame = oldController.view.frame;
    
        [oldController willMoveToParentViewController:nil];
        [self addChildViewController:newController];
    
        [self transitionFromViewController:oldController
                          toViewController:newController
                                  duration:1.0
                                   options:UIViewAnimationOptionTransitionCrossDissolve
                                animations:^{
                                    // no further animations required
                                }
                                completion:^(BOOL finished) {
                                    [oldController removeFromParentViewController];
                                    [newController didMoveToParentViewController:self];
                                }];
    }
    
  4. Y los controladores hijos ahora pueden usar este protocolo en referencia a la propiedad self.parentViewController que iOS le proporciona:

    - (IBAction)didTouchUpInsideButton:(id)sender
    {
        id <ContainerParent> parentViewController = (id)self.parentViewController;
        NSAssert([parentViewController respondsToSelector:@selector(changeContentTo:)], @"Parent must conform to ContainerParent protocol");
    
        UIViewController *newChild = ... // instantiate the new child controller any way you want
        [parentViewController changeContentTo:newChild];
    }
    
 38
Author: Rob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-09 13:27:00

Para este tipo de transición también puede usar animaciones UIView.animateWith....

Por ejemplo, supongamos que rootContainerView es contenedor y contentViewController controlador actualmente activo en contenedor, entonces

func setContentViewController(contentViewController:UIViewController, animated:Bool = true) {
    if animated == true {
        addChildViewController(contentViewController)
        contentViewController.view.alpha = 0
        contentViewController.view.frame = rootContainerView.bounds
        rootContainerView.addSubview(contentViewController.view)
        self.contentViewController?.willMoveToParentViewController(nil)

        UIView.animateWithDuration(0.3, animations: {
            contentViewController.view.alpha = 1
            }, completion: { (_) in
                contentViewController.didMoveToParentViewController(self)

                self.contentViewController?.view.removeFromSuperview()
                self.contentViewController?.didMoveToParentViewController(nil)
                self.contentViewController?.removeFromParentViewController()
                self.contentViewController = contentViewController
        })
    } else {
        cleanUpChildControllerIfPossible()

        contentViewController.view.frame = rootContainerView.bounds
        addChildViewController(contentViewController)
        rootContainerView.addSubview(contentViewController.view)
        contentViewController.didMoveToParentViewController(self)
        self.contentViewController = contentViewController
    }
}

// MARK: - Private

private func cleanUpChildControllerIfPossible() {
    if let childController = contentViewController {
        childController.willMoveToParentViewController(nil)
        childController.view.removeFromSuperview()
        childController.removeFromParentViewController()
    }
}

Esto proporcionará u animaciones de fundido simples, u también puede probar cualquier UIViewAnimationOptions, transiciones, etc.

 2
Author: gbk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-17 19:29:46

Parece que te estás confundiendo. Un contentView (asumiendo UIView) no 'contiene' un controlador que usted intercambiaría. A UIViewController maneja su UIView. Me parece que necesita una configuración de controlador de vista padre-hijo.

Un controlador de vista padre único, que manejaría controladores de vista hijos, cada uno de los cuales puede manejar cuando se muestra en la pantalla y ajustar sus UIViews y contenido en consecuencia. Consulte la documentación de Apple a continuación.

Contenedor Programación-Documentación de Apple

 -2
Author: Tim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-03 15:27:16