bar ios objective-c

ios - bar - Presentar el controlador de vista modalmente en el lanzamiento de la aplicación



ion title center (3)

Entiendo que puedo configurar mi controlador raíz dinámicamente en applicationDidFinishLaunchingWithOptions: pero específicamente quiero presentar la pantalla de configuración de manera modal, de modo que cuando el usuario haya terminado con ella, la descarte y se revele la verdadera primera vista de la aplicación.

Tengo dos sugerencias. Una es intentar hacer esto en viewDidAppear: Lo intenté y aunque ves la vista del controlador de vista raíz si la observas con atención, apenas la ves y, a veces, no la ves en absoluto si parpadeas:

-(void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [self presentViewController:[self.storyboard instantiateViewControllerWithIdentifier:@"setupViewController"] animated:NO completion:NULL]; }

Por supuesto, debe agregar un indicador para que no lo haga cada vez que se viewDidAppear: lo contrario, nunca podrá volver a este controlador de vista! Pero eso es trivial y lo dejo como un ejercicio para el lector.

Mi otra sugerencia, y usted ha pensado claramente en hacer esto, es usar un controlador de vista integrado (secundario) personalizado en su lugar. Eso funciona en torno a las limitaciones de toda la "presentación".

Iniciaría y configuraría las cosas dinámicamente, como usted dice, con el controlador de vista secundario presente si fuera necesario, configurándolo todo durante el proceso de inicio. La vista del controlador de vista secundaria solo cubriría la vista del controlador de vista raíz. Así que eso es lo que el usuario vería cuando se inicie la aplicación.

Y luego, cuando el procedimiento de configuración del usuario finaliza y el usuario "descarta" esta vista, se corta esa vista con animación y se quita el controlador de vista secundario, lo que revela la vista del controlador de vista raíz que se encuentra debajo. La animación hará que todo esto sea indistinguible de la eliminación de la vista presentada, aunque no sea realmente una.

Mi aplicación tiene una pantalla de configuración que se debe presentar modalmente en el controlador de vista raíz si se cumplen ciertas condiciones.

He mirado a mi alrededor en SO e Internet y la respuesta más cercana hasta ahora a cómo hacer esto es aquí:

AppDelegate, rootViewController y presentViewController

Hay 2 problemas con este enfoque sin embargo:

  1. En iOS 8, hacerlo de esta manera hace que aparezca un registro en la consola, lo que no parece ser un error, pero probablemente no sea bueno:

Unbalanced calls to begin/end appearance transitions for UITabBarController: 0x7fe20058d570.

  1. El controlador de vista raíz en realidad se muestra muy brevemente cuando se inicia la aplicación, y luego se desvanece en el controlador de vista presentado (aunque llamo explícitamente animated:NO en mi método presentViewController ).

Entiendo que puedo configurar mi controlador raíz dinámicamente en applicationDidFinishLaunchingWithOptions: pero específicamente quiero presentar la pantalla de configuración de manera modal, de modo que cuando el usuario haya terminado con ella, la descarte y se revele la verdadera primera vista de la aplicación. Es decir, no quiero cambiar dinámicamente mi controlador de vista de raíz a mi pantalla de configuración y presentar la experiencia de mi aplicación de manera modal cuando el usuario haya terminado de configurar.

Al presentar el controlador de vista en el método viewDidLoad mi controlador de vista de raíz, también se produce un parpadeo notable de la interfaz de usuario cuando la aplicación se inicia por primera vez.

¿Es posible presentar un controlador de vista de manera programática, antes de que la aplicación haya procesado algo para que la primera vista en su lugar sea el controlador de vista modal?

ACTUALIZACIÓN: Gracias por los comentarios, agregando mi código actual como se sugiere:

En mi AppDelegate.m :

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil]; [self.window makeKeyAndVisible]; [self.window.rootViewController presentViewController:[storyboard instantiateViewControllerWithIdentifier:@"setupViewController"] animated:NO completion:NULL]; return YES; }

Esto hace lo que necesito, excepto por el hecho de que muestra brevemente el controlador de la vista raíz de la ventana por un segundo cuando la aplicación se inicia, luego se desvanece el setupViewController, lo que me parece extraño dado que lo estoy presentando sin animación y el desvanecimiento no es como un modal Ver controlador se presenta de todos modos.

Lo único que me ha acercado es agregar manualmente la vista en la vista del controlador de vista raíz. El método de carga fue así:

- (void)viewDidLoad { [self.view addSubview:setupViewController.view]; [self addChildViewController:setupViewController]; }

El problema con este enfoque es que ya no puedo descartar "nativamente" el controlador SetupView, y ahora tendré que lidiar con la jerarquía de vistas y animarlo, lo cual está bien si es la única solución, pero esperaba que hubiera un problema. forma autorizada de agregar modalmente un controlador de vista sin animación antes de que se muestre el controlador de vista raíz.

ACTUALIZACIÓN 2: Después de probar muchas cosas y esperar una respuesta durante 2 meses, esta pregunta propone la solución más creativa:

iOS Presentar el controlador de vista modal en el inicio sin flash

Supongo que es hora de aceptar que simplemente no es posible presentar una vista modalmente sin animación antes de que aparezca el controlador de vista raíz. Sin embargo, la sugerencia en ese hilo es crear una instancia de su Pantalla de inicio y dejarla encendida durante más tiempo que el predeterminado hasta que el controlador de vista modal tenga la oportunidad de presentarse.


Supongo que es hora de aceptar que simplemente no es posible presentar una vista modalmente sin animación antes de que aparezca el controlador de vista raíz.

Antes de que aparezca, no, no puedes presentarte . Pero hay múltiples enfoques válidos para resolver esto visualmente. Recomiendo la solución A a continuación por su simplicidad.

A. agregar launchScreen como subvista, luego presentar, luego eliminar launchscreen

La solución se presenta aquí por ullstrm y no sufre llamadas no balanceadas para comenzar / finalizar las transiciones de apariencia :

let launchScreenView = UIStoryboard(name: "LaunchScreen", bundle: nil).instantiateInitialViewController()!.view! launchScreenView.autoresizingMask = [.flexibleWidth, .flexibleHeight] launchScreenView.frame = window!.rootViewController!.view.bounds window?.rootViewController?.view.addSubview(launchScreenView) window?.makeKeyAndVisible() // avoiding: Unbalanced calls to begin/end appearance transitions. DispatchQueue.global().async { DispatchQueue.main.async { self.window?.rootViewController?.present(myViewControllerToPresent, animated: false, completion: { launchScreenView.removeFromSuperview() }) } }

B. addChildViewController primero, luego eliminar, luego presentar

La solución se presenta aquí por Benedict Cohen .


Estaba en el mismo barco que tú y encontré la misma respuesta. Aprendí que puede deshacerse del primer problema (advertencia de llamadas no balanceadas) configurando el estilo modalPresentationStyle de modalPresentationStyle de su setupViewController en .OverCurrentContext o .OverFullScreen . Problema resuelto - así lo pensé.

Solo más tarde noté el segundo problema y eso era algo con lo que no podía vivir ... de vuelta al punto de partida.

Como usted, quería una solución con una jerarquía de vista normal y no quería "falsificar" algo. Creo que la solución más elegante es cambiar el control raíz de windowsViewController en el primer despido de su setupViewController.

Por lo tanto, en el inicio, configura setupViewController como rootViewController (si es necesario):

var window: UIWindow? var tabBarController: UITabBarController! func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { tabBarController = window!.rootViewController as! UITabBarController if needsToShowSetup() { let setupViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("SetupViewController") as! SetupViewController window?.rootViewController = setupViewController } return true }

Cuando finaliza la instalación, llama a un método en su appDelegate para cambiar al rootViewController ''real'':

func switchToTabBarController() { let setupUpViewController = window!.rootViewController! tabBarController.view.frame = window!.bounds window!.insertSubview(tabBarController.view, atIndex: 0) let height = setupUpViewController.view.bounds.size.height UIView.animateWithDuration(0.5, delay: 0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1, options: .allZeros, animations: { () -> Void in setupUpViewController.view.transform = CGAffineTransformMakeTranslation(0, height) }) { (completed) -> Void in self.window!.rootViewController = self.tabBarController } }

Estaba después de una animación de ''portada vertical''. Para crossfade y otros, puede usar UIView.transitionFromView(fromView: UIView, toView: UIView...) . De aquí en adelante, puede presentar / descartar su controlador de configuración de la manera normal, por lo que su acción doneButton podría ser algo como esto:

@IBAction func doneButtonSelected(sender: UIButton) { if presentingViewController != nil { presentingViewController!.dismissViewControllerAnimated(true, completion: nil) } else { let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate appDelegate.switchToTabBarController() } }

En realidad, implementé esto a través de la delegación, y la delegación de aplicaciones fue el delegado la primera vez.