iOS 8 SDK: UIWebView modal y selector de cámara / imagen

He descubierto que, al comstackr para iOS 8 (y que se ejecuta en iOS 8), un UIWebView no puede mostrar el selector de cámara / imagen si el UIWebView está en un controlador de vista presentado de forma modal. Funciona sin problemas para ver los controladores directamente “colgando” de la ventana rootViewController o para ver los controladores que rootViewController de ella.

La aplicación de prueba se puede encontrar en https://dl.dropboxusercontent.com/u/6214425/TestModalWebCamera.zip pero la describiré a continuación.

Mi aplicación de prueba (construida con guiones gráficos, pero la aplicación real no los usa) tiene dos controladores de vista (sin nombre original ViewController y ViewController2 ). ViewController está contenido en un UINavigationController que es el controlador de vista raíz. ViewController contiene un UIWebView (funciona bien), un botón que “muestra” (“empuja”) ViewController2 y un UIBarButtonItem que presenta UIBarButtonItem ViewController2 . ViewController2 tiene otra UIWebView que funciona cuando se “empuja” pero no cuando se “presenta”.

Tanto ViewController como ViewController2 se cargan con:

 - (void)viewDidLoad { [super viewDidLoad]; [self.webView loadHTMLString:@"" baseURL:nil]; } 

Al intentar usar el modal, UIWebView Xcode imprime lo siguiente en la consola y descarta la aplicación modal:

 Warning: Attempt to present  on  whose view is not in the window hierarchy! 

Mi teoría actual es que los cambios en UIActionSheet a UIAlertController pueden haber producido esta situación, pero es bastante difícil de probar. Abriré un Radar con Apple, por si acaso.

¿Alguien ha encontrado la misma situación y alguna solución alternativa?

Descubrí que en iOS 8.0.2 iPad no parece tener ese error, pero iPhone todavía lo tiene.

Sin embargo, reemplazando a continuación en el controlador de vista que contiene el uiwebview

 -(void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion 

Y comprobar que hay un ViewViewController presentado parece funcionar.

Pero necesita verificar los efectos secundarios

 #import "UiWebViewVC.h" @interface UiWebViewVC () @property (weak, nonatomic) IBOutlet UIWebView *uiwebview; @end @implementation UiWebViewVC - (void)viewDidLoad { [super viewDidLoad]; NSURL *url = [NSURL URLWithString:@"http://html5demos.com/file-api-simple"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; self.uiwebview.scalesPageToFit = YES; [self.uiwebview loadRequest:request]; } -(void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion { if ( self.presentedViewController) { [super dismissViewControllerAnimated:flag completion:completion]; } } @end 

Tengo el mismo problema en iOS 9. Intente agregar ivar ‘_flag’ y luego anule este método en el controlador de vista con UIWebView

 #pragma mark - Avoiding iOS bug - (UIViewController *)presentingViewController { // Avoiding iOS bug. UIWebView with file input doesn't work in modal view controller if (_flagged) { return nil; } else { return [super presentingViewController]; } } - (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion { // Avoiding iOS bug. UIWebView with file input doesn't work in modal view controller if ([viewControllerToPresent isKindOfClass:[UIDocumentMenuViewController class]] ||[viewControllerToPresent isKindOfClass:[UIImagePickerController class]]) { _flagged = YES; } [super presentViewController:viewControllerToPresent animated:flag completion:completion]; } - (void)trueDismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion { // Avoiding iOS bug. UIWebView with file input doesn't work in modal view controller _flagged = NO; [self dismissViewControllerAnimated:flag completion:completion]; } 

Esto funciona bien para mí

Estaba teniendo un problema similar y descubrí que los elementos de UIWebView en IOS no son compatibles con el elemento html:

No estoy seguro de por qué Apple eligió no admitir este importante elemento html, pero estoy seguro de que tienen sus razones. (Aunque este elemento funciona perfectamente en Safari en iOS).

En muchos casos, cuando el usuario hace clic en este tipo de botón en un UIWebView, les permitirá tomar / elegir una foto. SIN EMBARGO, el UIWebView en IOS no tiene la capacidad de adjuntar archivos como este en los datos POST cuando se envía el formulario.

La solución: para lograr la misma tarea, puede crear una forma similar en InterfaceBuilder con un botón que activa el UIImagePickerController. Luego, usted crea una solicitud HTTP POST con todos los datos del formulario y la imagen. No es tan difícil como parece, consulte el siguiente enlace para obtener un código de muestra que hace el trabajo: ios Subir imagen y texto usando HTTP POST

Para mí, tiendo a tener un UINavigationController personalizado para que múltiples vistas puedan compartir la misma lógica. Así que para mi solución (en Swift) aquí está lo que puse en mi NavigationController personalizado.

 import UIKit class NavigationController: UINavigationController { override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } override func dismissViewControllerAnimated(flag: Bool, completion: (() -> Void)?) { if let vc = self.presentedViewController { // don't bother dismissing if the view controller being presented is a doc/image picker if !vc.isKindOfClass(UIDocumentMenuViewController) || !vc.isKindOfClass(UIImagePickerController) { super.dismissViewControllerAnimated(flag, completion:completion) } } } override func viewDidLoad() { super.viewDidLoad() // make sure that the navigation controller can't be dismissed self.view.window?.rootViewController = self } } 

Esto significa que puede tener montones de controladores de vista con vistas web y todos funcionarán con el elemento de carga de archivos.

Bien, aquí está la solución que terminé usando. Es un cambio de 2 minutos, bastante hacky, pero funciona como se esperaba y evita el error que todos estamos teniendo. Básicamente, en lugar de presentar el controlador de vista hijo modalmente, lo configuro en el control raíz de la ventana y mantengo una referencia al controlador principal. Entonces, donde solía tener esto (en el controlador de vista padre):

 presentViewController(newController, animated: true, completion: nil) 

Ahora tengo esto

 view.window?.rootViewController = newController 

En ambos casos, el controlador newController es el delegado de newController , que es la forma en que guardo la referencia.

 newController.delegate = self 

Finalmente, en mi retrollamada de delegado para cerrar el modal, donde solía tener esto:

 dismissViewControllerAnimated(true, completion: nil) 

Ahora tengo esto:

 viewController.view.window?.rootViewController = self 

De modo que obtenemos el mismo efecto que un controlador de vista que se apodera completamente de la pantalla, y luego cede su control cuando termina. En este caso, sin embargo, no está animado. Tengo la sensación de que la transición del controlador no sería muy difícil con algunas de las animaciones integradas.

Esperamos que ya esté utilizando el patrón de delegado para administrar la comunicación con el controlador modal, según las recomendaciones de Apple, pero si no lo está, estoy seguro de que puede usar cualquier cantidad de métodos que conserven una referencia y reciban una llamada.

Mi solución es crear un controlador de visualización personalizado con presentación modal personalizada basada en la jerarquía padre-hijo de los controladores. La animación será la misma para que el usuario no note la diferencia.

Mis sugerencias para la animación:

 animateWithDuration:(animated ? 0.6 : 0.0) delay:0.0 usingSpringWithDamping:1.f initialSpringVelocity:0.6f options:UIViewAnimationOptionCurveEaseOut 

Se verá exactamente como la animación de presentación modal por defecto en iOS7 / 8.

Lo que hice cada vez que cambia el controlador de vista, convierte la vista de destino en la raíz.

desde el controlador de la primera vista:

 let web = self.storyboard?.instantiateViewController(withIdentifier:"web") as! UINavigationController view.window?.rootViewController = web 

así es como paso la raíz al otro, y cuando regreso al primero, hice esto.

desde el controlador de vista web:

 let first = self.storyboard?.instantiateViewController(withIdentifier: "reveal") as! SWRevealViewController present(first, animated: true, completion: nil) 

y todo está bien, puedo mostrar el modal para seleccionar una foto de la biblioteca, tomar fotos y todo lo demás.

No sé si es una buena práctica, pero funciona bien en emulador y dispositivo.

Espero eso ayude.