iOS 8 SDK: modal UIWebView y selector de cámara/imagen
objective-c ios8 (7)
He encontrado que, al compilar para iOS 8 (y ejecutar 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 manera modal.
Funciona sin problemas en los controladores de vista directamente "colgando" de la ventana
rootViewController
o en los controladores de vista empujados desde ella.
La aplicación de prueba se puede encontrar en https://dl.dropboxusercontent.com/u/6214425/TestModalWebCamera.zip pero lo 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 (llamados originalmente
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
ViewController2
modal.
ViewController2
tiene otro
UIWebView
que funciona cuando se "empuja" pero no cuando se "presenta".
Tanto
ViewController
como
ViewController2
están cargados con:
- (void)viewDidLoad {
[super viewDidLoad];
[self.webView loadHTMLString:@"<input type=/"file/" accept=/"image/*;capture=camera/">" baseURL:nil];
}
Al intentar usar el modal
UIWebView
Xcode imprime lo siguiente en la consola y descarta el modal de la aplicación:
Warning: Attempt to present <UIImagePickerController: 0x150ab800> on <ViewController2: 0x14623580> whose view is not in the window hierarchy!
Mi teoría actual es que los cambios en
UIActionSheet
a
UIAlertController
podrían 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?
Descubrí que en iOS 8.0.2 el iPad no parece tener ese error, pero el iPhone todavía lo tiene.
Sin embargo, anulando el siguiente en el controlador de vista que contiene la uiwebview
-(void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion
Y comprobar que hay un PresentViewController parece funcionar.
Pero es necesario 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
Estaba teniendo un problema similar y descubrí que los elementos UIWebView en IOS no admiten el elemento html:
No estoy seguro de por qué Apple decidió no admitir este elemento html IMPORTANTE, 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 realizar la misma tarea, puede crear un formulario similar en InterfaceBuilder con un botón que active el UIImagePickerController. Luego, 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 ver un código de muestra que hace el trabajo: ios Cargar imagen y texto usando HTTP POST
Lo que hice cada vez que cambia el controlador de vista, convierte la vista de destino en la raíz.
desde el primer controlador de vista:
let web = self.storyboard?.instantiateViewController(withIdentifier:"web") as! UINavigationController
view.window?.rootViewController = web
así es como paso la raíz a la otra, y cuando regreso a la primera, 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 una foto y todo lo demás.
No sé si es una buena práctica, pero funciona bien en emulador y dispositivo.
Espero eso ayude.
Mi solución es crear un controlador de vista personalizado con una presentación modal personalizada basada en la jerarquía de controladores hijo-padre. 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 predeterminada en iOS7 / 8.
Muy 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 tenemos. Básicamente, en lugar de presentar el controlador de vista secundario modalmente, lo configuro en rootViewController de la ventana y mantengo una referencia al controlador principal. Entonces, donde solía tener esto (en el controlador de vista principal):
presentViewController(newController, animated: true, completion: nil)
Ahora tengo esto
view.window?.rootViewController = newController
En ambos casos, el controlador
newController
es el delegado de
newController
, y así es como mantengo la referencia.
newController.delegate = self
Finalmente, en mi devolución de llamada delegada para cerrar el modal, donde solía tener esto:
dismissViewControllerAnimated(true, completion: nil)
Ahora tengo esto:
viewController.view.window?.rootViewController = self
Entonces obtenemos el mismo efecto de un controlador de vista que se apodera completamente de la pantalla y luego cede su control cuando está hecho. En este caso, sin embargo, no está animado. Tengo la sensación de que animar la transición del controlador no sería muy difícil con algunas de las animaciones de vista incorporadas.
Con suerte, ya está utilizando el patrón de delegado para administrar la comunicación con el controlador modal, según la recomendación de Apple, pero si no lo está, estoy seguro de que puede usar cualquier número de métodos que mantengan una referencia y que le devuelvan la llamada.
Para mí, tiendo a tener un UINavigationController personalizado para que múltiples vistas puedan compartir la misma lógica. Entonces, para mi solución (en Swift), esto es 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 un montón de controladores de vista con vistas web y todos trabajarán con el elemento de carga de archivos.
Tengo el mismo problema en iOS 9. Intente agregar ivar ''_flag'' y luego anule estos métodos 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 me funciona bien