iOS 8 SDK: mode UIWebView et caméra / capteur d'image
j'ai constaté que, lors de la compilation pour iOS 8 (et l'exécution dans iOS 8), un UIWebView
ne peut pas montrer la caméra/capteur d'image si le UIWebView
est dans un contrôleur de vue présenté modalement. Il fonctionne sans problème dans les contrôleurs de vue directement " suspendus "à partir de la fenêtre rootViewController
ou les contrôleurs de vue poussés à partir de celui-ci.
L'application d'essai se trouve à https://dl.dropboxusercontent.com/u/6214425/TestModalWebCamera.zip mais je allons décrire ci-dessous.
mon application d'essai (construite avec des storyboards, mais l'application réelle ne les utilise pas) a deux contrôleurs de vue (unoriginalement appelé ViewController
et ViewController2
). ViewController
est contenu dans un UINavigationController
qui est le contrôleur de la vue racine. ViewController
contient un UIWebView
(OK), un bouton qui "montre" ("pousse") ViewController2
, et un UIBarButtonItem
qui modal présente ViewController2
. ViewController2
a un autre UIWebView
qui fonctionne lorsque "poussé", mais pas quand "présenté".
les deux ViewController
et ViewController2
sont chargés avec:
- (void)viewDidLoad {
[super viewDidLoad];
[self.webView loadHTMLString:@"<input type="file" accept="image/*;capture=camera">" baseURL:nil];
}
en essayant d'utiliser le modal UIWebView
Xcode imprime ce qui suit dans la console et rejette l'app modal:
Warning: Attempt to present <UIImagePickerController: 0x150ab800> on <ViewController2: 0x14623580> whose view is not in the window hierarchy!
ma théorie actuelle est que les changements dans UIActionSheet
à UIAlertController
pourrait avoir produit cette situation, mais il est assez difficile de le prouver. Je vais ouvrir un Radar avec Apple, juste au cas où.
Quelqu'un a-t-il trouvé la même situation et une solution?
7 réponses
j'ai trouvé que dans iOS 8.0.2 iPad ne semble pas avoir ce bug, mais iPhone le fait toujours.
cependant, le contrôleur de vue contenant le UIWebView
-(void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion
et de vérifier qu'il y a un ViewController présenté semble fonctionner.
mais besoin de vérifier les effets secondaires
#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
j'ai le même problème sur iOS 9. Essayez d'ajouter ivar '_flag' et puis de modifier ces méthodes dans le contrôleur de vue avec 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];
}
Cela fonctionne pour moi d'amende
j'avais un problème similaire, et j'ai découvert que les éléments UIWebView dans IOS ne supportent pas l'élément html:
Je ne sais pas pourquoi Apple a choisi de ne pas prendre en charge cet élément html IMPORTANT, mais je suis sûr qu'ils ont leurs raisons. (Même si cet élément fonctionne parfaitement sur Safari sur IOS.)
dans de nombreux cas, lorsque l'utilisateur clique sur ce type de bouton dans un UIWebView, il pourra prendre/ choisir une photo. CEPENDANT, la UIWebView dans IOS n'a pas la capacité de joindre des fichiers comme celui-ci dans les données de poste lorsque le formulaire est soumis.
la Solution: pour accomplir la même tâche, vous pouvez créer une forme similaire dans InterfaceBuilder avec un bouton qui déclenche le contrôleur UIImagePickerController. Ensuite, vous créez une requête HTTP POST avec toutes les données du formulaire et l'image. Ce n'est pas aussi dur que ça en a l'air, consultez le lien ci-dessous pour un exemple de code qui permet de faire le travail: iOS Upload Image et texte en utilisant HTTP POST
pour moi j'ai tendance à avoir un UINavigationController personnalisé afin que plusieurs vues puissent partager la même logique. Donc, pour mon contournement (dans Swift) voici ce que j'ai mis dans mon Controller de navigation personnalisé.
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
}
}
cela signifie que vous pouvez avoir des charges de contrôleurs de vue avec webviews et ils fonctionneront tous avec l'élément de téléchargement de fichier.
voilà la solution que j'ai utilisée. C'est un changement de 2 minutes, assez hacky, mais fonctionne comme prévu et évite le bug que nous avons tous. Fondamentalement, plutôt que de présenter le contrôleur de vue d'enfant modalement, Je l'ai réglé sur le controller rootViewController de la fenêtre et j'ai gardé une référence au controller parent. Ainsi, lorsque j'avais ceci (dans le contrôleur de vue parent):
presentViewController(newController, animated: true, completion: nil)
j'ai maintenant cette
view.window?.rootViewController = newController
dans les deux cas, le le contrôleur parent est le délégué de newController
, c'est comme ça que je garde la référence.
newController.delegate = self
enfin, où dans mon appel de délégué pour fermer le modal, où j'avais l'habitude d'avoir ceci:
dismissViewControllerAnimated(true, completion: nil)
j'ai maintenant ceci:
viewController.view.window?.rootViewController = self
donc nous obtenons le même effet d'un contrôleur de vue qui prend entièrement le contrôle de l'écran, puis cède son contrôle quand il est fait. Dans ce cas, ce n'est pas animé. J'ai un sentiment animer la transition du contrôleur ne serait pas très difficile avec certaines des animations de vue intégrées.
J'espère que vous utilisez déjà le motif de délégué pour gérer la communication avec le contrôleur modal, selon la recommandation D'Apple, mais si vous ne l'êtes pas, je suis sûr que vous pouvez utiliser n'importe quel nombre de méthodes qui gardent une référence et obtenir rappelé.
ma solution est de créer un contrôleur de vue personnalisé avec une présentation modale personnalisée basée sur la hiérarchie enfant-parent des contrôleurs. L'Animation sera la même, de sorte que l'utilisateur ne remarquera pas la différence.
mes suggestions pour l'animation:
animateWithDuration:(animated ? 0.6 : 0.0)
delay:0.0
usingSpringWithDamping:1.f
initialSpringVelocity:0.6f
options:UIViewAnimationOptionCurveEaseOut
il ressemblera exactement à l'animation de présentation modale par défaut dans iOS7/8.
ce que j'ai fait chaque fois que le contrôleur de vue change, convertissez la vue de destination en root.
du contrôleur de première vue:
let web = self.storyboard?.instantiateViewController(withIdentifier:"web") as! UINavigationController
view.window?.rootViewController = web
c'est comme ça que je passe la racine à l'autre, et quand je reviens au premier j'ai fait ça.
du contrôleur web view:
let first = self.storyboard?.instantiateViewController(withIdentifier: "reveal") as! SWRevealViewController
present(first, animated: true, completion: nil)
et tout va bien, je peux montrer le modal pour choisir la photo de la bibliothèque, prendre la photo et tout autre.
je ne sais pas si c'est une bonne pratique mais qui fonctionne bien dans l'émulateur et l'appareil.
J'espère que ça aidera.