Pregunta

Estoy autoría de una aplicación para iPad. Una de las pantallas de la aplicación es perfectamente adecuado para el uso de un UISplitViewController. Sin embargo, el nivel superior de la aplicación es un menú principal, que no quiero utilizar un UISplitViewController para. Esto presenta un problema, porque el estado de Apple que:

  1. UISplitViewController debe ser el controlador de vista de nivel superior en la aplicación, es decir, su punto de vista se debe añadir como la subvista de UIWindow

  2. si se utiliza, UISplitViewController debería estar allí durante la vida útil de la aplicación - es decir, no retire su vista desde UIWindow y poner otro en su lugar, o viceversa

Después de haber leído alrededor y experimentó, parece única opción viable para satisfacer los requisitos de Apple y nuestra propia es el uso de cuadros de diálogo modales. Por lo que nuestra aplicación tiene un UISplitViewController en el nivel raíz (es decir, su punto de vista agregado como la subvista de UIWindow), y para mostrar nuestro menú principal, que presentarlo como una pantalla completa de diálogo modal en el UISplitViewController. A continuación, al desestimar la vista del menú principal controlador de diálogo modal, podemos mostrar nuestra realidad vista dividida.

Esta estrategia parece bien el trabajo. Sin embargo, se plantea la pregunta:

1) ¿Hay alguna forma mejor de estructuración esto, sin modales, que también cumple con todos los requisitos mencionados? Parece un poco extraño tener la interfaz de usuario principal aparece en virtud de ser empujado como un cuadro de diálogo modal. (Modals se supone que son para las tareas del usuario enfocadas.)

2) ¿Corro el riesgo de rechazo tienda de aplicaciones debido a mi enfoque? Esta estrategia modal es, probablemente, 'haciendo un uso indebido' diálogos modales, según las directrices de interfaz humana de Apple. Pero ¿qué otra opción me han dado? Iban a saber que estoy haciendo esto, de todos modos?

¿Fue útil?

Solución

Touche! Corrió en el mismo tema y resuelto de la misma manera usando modales. En mi caso fue una vista de inicio de sesión y luego el menú principal, así que se muestra antes de la SplitView. He utilizado la misma estrategia que pensado por ti. I (así como varias otras personas con conocimientos de iOS que hablé) no pude encontrar una mejor manera a cabo. Funciona bien para mí. El usuario nunca se da cuenta de la de todas formas modales. Presentarlos de modo. Y sí, yo también puedo decir que hay un buen número de aplicaciones que hacen lo mismo bajo los trucos capucha en la App Store. :) Por otro lado, hágamelo saber si la figura de una manera mejor de alguna manera de alguna manera en algún momento:)

Otros consejos

En serio no creía que este concepto de tener alguna UIViewController para mostrar antes UISplitViewController (formulario de acceso, por ejemplo) resulta ser tan complicado, hasta que tuve que crear ese tipo hiearchy de vista.

Mi ejemplo se basa en iOS 8 y XCode 6.0 (Swift), así que no estoy seguro de si existía este problema antes de una misma manera, o es debido a algunos nuevos errores introducidos con iOS 8, pero a partir de todas las preguntas similares que he encontrado, no vi solución completa 'no poco limpia' a este problema.

Yo te guiaré a través de algunas de las cosas que he tratado antes de que terminé con una solución (al final de este post). Cada ejemplo se basa en la creación de nuevo proyecto de una plantilla maestra-detalle sin CoreData habilitado.


primer intento (segue modal para UISplitViewController):

  1. crear nueva subclase UIViewController (LoginViewController por ejemplo)
  2. añadir nuevo controlador de vista en guión gráfico, configurarlo como vista inicial del controlador (en lugar de UISplitViewController) y conectarlo a LoginViewController
  3. añadir a UIButton LoginViewController y crear segue modal a partir de ese botón para UISplitViewController
  4. movimiento repetitivo código de configuración para UISplitViewController de didFinishLaunchingWithOptions de AppDelegate a prepareForSegue de LoginViewController

Esta casi funcionó. Digo casi, porque después de la aplicación se inicia con LoginViewController y botón y segue a UISplitViewController toque, hay un error extraño está pasando:. mostrar y ocultar vista controlador maestro sobre el cambio de orientación ya no está animado

Después de algún tiempo luchando con este problema y sin solución real, pensé que está conectado de alguna manera con que extraña regla que UISplitViewController debe ser RootViewController (y en este caso no lo es, LoginViewController es) así que me di por vencido de esta solución no tan perfecta.


segundo intento (segue modal de UISplitViewController):

  1. crear nueva subclase UIViewController (LoginViewController por ejemplo)
  2. añadir nuevo controlador de vista de guión gráfico, y conectarlo a LoginViewController (pero esta vez la licencia UISplitViewController para ser controlador de vista inicial)
  3. crear segue modal de la UISplitViewController a LoginViewController
  4. añadir a UIButton LoginViewController y crear segue desenrollado de ese botón

Por último, añadir el código a didFinishLaunchingWithOptions de AppDelegate después de código estándar para la creación de UISplitViewController:

window?.makeKeyAndVisible()
splitViewController.performSegueWithIdentifier("segueToLogin", sender: self)
return true

O prueba con este código en su lugar:

window?.makeKeyAndVisible()
let loginViewController = splitViewController.storyboard?.instantiateViewControllerWithIdentifier("LoginVC") as LoginViewController
splitViewController.presentViewController(loginViewController, animated: false, completion: nil)
return true

Estos dos ejemplos producen mismas cosas varias malas:

  1. salidas de la consola: Unbalanced calls to begin/end appearance transitions for <UISplitViewController: 0x7fc8e872fc00>
  2. UISplitViewController debe demostrarse primero antes de LoginViewController se segued forma modal (I preferiría presente sólo el formulario de acceso de modo que el usuario no ve UISplitViewController antes conectado)
  3. Relájese segue no quede llama (esto es totalmente otro error, y no voy a entrar en esa historia ahora)

Solución (actualización RootViewController)

La única manera que he encontrado que funciona correctamente es si cambia RootViewController de ventana sobre la marcha:

  1. Definir Storyboard ID para LoginViewController y UISplitViewController, y añadir algún tipo de propiedad loggedin a AppDelegate.
  2. Sobre la base de esta propiedad, el controlador instantiate vista apropiada y después de ese conjunto como RootViewController.
  3. hacerlo sin la animación en didFinishLaunchingWithOptions pero animado cuando se llama desde la interfaz de usuario.

A continuación se muestra el código de AppDelegate:

var loggedIn = false

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    setupRootViewController(false)
    return true
}

func setupRootViewController(animated: Bool) {
    if let window = self.window {
        var newRootViewController: UIViewController? = nil
        var transition: UIViewAnimationOptions

        // create and setup appropriate rootViewController
        if !loggedIn {
            let loginViewController = window.rootViewController?.storyboard?.instantiateViewControllerWithIdentifier("LoginVC") as LoginViewController
            newRootViewController = loginViewController
            transition = .TransitionFlipFromLeft

        } else {
            let splitViewController = window.rootViewController?.storyboard?.instantiateViewControllerWithIdentifier("SplitVC") as UISplitViewController
            let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as UINavigationController
            navigationController.topViewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem()
            splitViewController.delegate = self

            let masterNavigationController = splitViewController.viewControllers[0] as UINavigationController
            let controller = masterNavigationController.topViewController as MasterViewController

            newRootViewController = splitViewController
            transition = .TransitionFlipFromRight
        }

        // update app's rootViewController
        if let rootVC = newRootViewController {
            if animated {
                UIView.transitionWithView(window, duration: 0.5, options: transition, animations: { () -> Void in
                    window.rootViewController = rootVC
                    }, completion: nil)
            } else {
                window.rootViewController = rootVC
            }
        }
    }
}

Y este es el código de ejemplo de LoginViewController:

@IBAction func login(sender: UIButton) {
    let delegate = UIApplication.sharedApplication().delegate as AppDelegate
    delegate.loggedIn = true
    delegate.setupRootViewController(true)
}

También me gustaría saber si hay alguna manera mejor / más limpio para que esto funcione correctamente en iOS 8.

Y quién dijo que puede tener sólo una ventana? :)

A ver si mi respuesta en esta pregunta similar ayuda lata.

Este enfoque está funcionando muy bien para mí. Mientras que usted no tiene que preocuparse de varias pantallas o restauración del estado, el código relacionado debería ser suficiente para hacer lo que necesita: usted no tiene que hacer sus espaldas mirada lógicas o reescribir el código existente, y todavía puede aprovechar del UISplitView en un nivel más profundo dentro de la aplicación -. sin (que yo sepa) romper las directrices de Apple

Para los futuros desarrolladores de iOS que se ejecutan en el mismo tema: aquí hay otra respuesta y explicaciones. Usted tiene que hacer es controlador de vista raíz. Si no lo es, una superposición modal.

UISplitviewcontroller no como un rootview controlador

Sólo se encontró con este problema en un proyecto y pensé en compartir mi solución. En nuestro caso (para iPad), queríamos empezar con un UISplitViewController con los dos controladores de vista visible (utilizando preferredDisplayMode = .allVisible). En algún punto de la jerarquía detalle (derecha) (teníamos un controlador de navegación por este lado, también) que queríamos para impulsar un nuevo controlador de vista sobre todo el controlador de vista dividida (no usar una transición modal).

En el iPhone, este comportamiento viene de tan libre de un solo controlador de vista es visible en cualquier momento. Pero en el iPad tuvimos que figura algo más hacia fuera. Terminamos yendo con un controlador de vista contenedor raíz que se suma el controlador de vista dividida a ella como un controlador de vista del niño. Este controlador de vista raíz está incrustado en un control de navegación. Cuando la vista del controlador de detalle en el controlador de la vista dividida quiere impulsar un nuevo controlador a través de todo el controlador de vista dividida, el controlador de vista raíz empuja este nuevo controlador de vista con su controlador de navegación.

Me gustaría aportar mi acercamiento a la presentación de un UISplitViewController, como le gustaría a través de -presentViewController:animated:completion: (todos sabemos que no va a funcionar, aunque). He creado una subclase UISplitViewController que responde a:

-presentAsRootViewController
-returnToPreviousViewController

La clase, que al igual que otros enfoques exitosos, como establece el UISplitViewController RootViewController de la ventana, pero lo hace con una animación similar a lo que se obtiene (por defecto) con -presentViewController:animated:completion:

PresentableSplitViewController.h

#import <UIKit/UIKit.h>    
@interface PresentableSplitViewController : UISplitViewController    
- (void) presentAsRootViewController;
@end

PresentableSplitViewController.m

#import "PresentableSplitViewController.h"

@interface PresentableSplitViewController ()
@property (nonatomic, strong) UIViewController *previousViewController;
@end

@implementation PresentableSplitViewController

- (void) presentAsRootViewController {

    UIWindow *window=[[[UIApplication sharedApplication] delegate] window];
    _previousViewController=window.rootViewController;

    UIView *windowSnapShot = [window snapshotViewAfterScreenUpdates:YES];
    window.rootViewController = self;

    [window insertSubview:windowSnapShot atIndex:0];

    CGRect dstFrame=self.view.frame;

    CGSize offset=CGSizeApplyAffineTransform(CGSizeMake(0, 1), window.rootViewController.view.transform);
    offset.width*=self.view.frame.size.width;
    offset.height*=self.view.frame.size.height;
    self.view.frame=CGRectOffset(self.view.frame, offset.width, offset.height);

    [UIView animateWithDuration:0.5
                          delay:0.0
         usingSpringWithDamping:1.0
          initialSpringVelocity:0.0
                        options:UIViewAnimationOptionCurveEaseInOut
                     animations:^{
                         self.view.frame=dstFrame;
                     } completion:^(BOOL finished) {
                         [windowSnapShot removeFromSuperview];
                     }];
}

- (void) returnToPreviousViewController {
    if(_previousViewController) {

        UIWindow *window=[[[UIApplication sharedApplication] delegate] window];

        UIView *windowSnapShot = [window snapshotViewAfterScreenUpdates:YES];
        window.rootViewController = _previousViewController;

        [window addSubview:windowSnapShot];

        CGSize offset=CGSizeApplyAffineTransform(CGSizeMake(0, 1), window.rootViewController.view.transform);
        offset.width*=windowSnapShot.frame.size.width;
        offset.height*=windowSnapShot.frame.size.height;

        CGRect dstFrame=CGRectOffset(windowSnapShot.frame, offset.width, offset.height);

        [UIView animateWithDuration:0.5
                              delay:0.0
             usingSpringWithDamping:1.0
              initialSpringVelocity:0.0
                            options:UIViewAnimationOptionCurveEaseInOut
                         animations:^{
                             windowSnapShot.frame=dstFrame;
                         } completion:^(BOOL finished) {
                             [windowSnapShot removeFromSuperview];
                             _previousViewController=nil;
                         }];
    }
}

@end

Me hizo un UISplitView como vista inicial, de lo que va de forma modal a una pantalla completa UIView y de nuevo a UISplitView. Si necesita volver a la SplitView usted tiene que utilizar un segue personalizado.

Leer este enlace (traducirlo a partir japonés)

UIViewController a UISplitViewController

Añadir a la respuesta de @tadija Estoy en una situación similar:

Mi aplicación fue sólo para los teléfonos, y añado un interfaz de usuario de la tableta. Me decidí hacerlo en Swift en la misma aplicación -. Y finalmente migrar toda la aplicación para utilizar el mismo guión gráfico (cuando siento la versión IPad es estable, que utilizan para los teléfonos deben ser trivial con las nuevas clases de XCode6)

No hay segues se define en mi escena con todo y que todavía funciona.

Mi el código en mi aplicación es delegado en ObjectiveC, y es ligeramente diferente - pero utiliza la misma idea. Tenga en cuenta que estoy usando el controlador de vista por defecto de la escena, a diferencia de los ejemplos anteriores. Siento Esto también trabajar en iOS7 / iPhone en el que el tiempo de ejecución generará un UINavigationController regular en lugar de un UISplitViewController. Incluso podría añadir nuevo código que impulsará la vista del controlador de inicio de sesión en iPhones, en lugar de cambiar el rootVC.

- (void) setupRootViewController:(BOOL) animated {
    UIViewController *newController = nil;
    UIStoryboard *board = [UIStoryboard storyboardWithName:@"Storyboard" bundle:nil];
    UIViewAnimationOptions transition = UIViewAnimationOptionTransitionCrossDissolve;

    if (!loggedIn) {
        newController = [board instantiateViewControllerWithIdentifier:@"LoginViewController"];
    } else {
        newController = [board instantiateInitialViewController];
    }

    if (animated) {
        [UIView transitionWithView: self.window duration:0.5 options:transition animations:^{
            self.window.rootViewController = newController;
            NSLog(@"setup root view controller animated");
        } completion:^(BOOL finished) {
            NSLog(@"setup root view controller finished");
        }];
    } else {
        self.window.rootViewController = newController;
    }
}

Otra opción: En la vista de detalles controlador I mostrar un controlador de vista modal:

let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
if (!appDelegate.loggedIn) {
    // display the login form
    let storyboard = UIStoryboard(name: "Storyboard", bundle: nil)
    let login = storyboard.instantiateViewControllerWithIdentifier("LoginViewController") as UIViewController
    self.presentViewController(login, animated: false, completion: { () -> Void in
       // user logged in and is valid now
       self.updateDisplay()
    })
} else {
    updateDisplay()
}

No descarte el controlador de acceso sin establecer el indicador de inicio de sesión. Nótese que en el controlador iPhones vista maestra vendrá primero, por lo que un código muy similar tendrá que ser en el controlador de la vista principal.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top