J'ai constaté que, lors de la compilation pour iOS 8 (et sous iOS 8), une UIWebView
ne peut pas afficher le sélecteur de caméra/d'image si la UIWebView
est dans un contrôleur de vue présenté sous forme modale. Cela fonctionne sans problème en vue des contrôleurs de vue directement "suspendus" à la fenêtre rootViewController
ou des contrôleurs de vue poussés à partir de celle-ci.
L'application test peut être trouvée à https://dl.dropboxusercontent.com/u/6214425/TestModalWebCamera.Zip mais je vais le décrire ci-dessous.
Mon application de test (construite avec des storyboards, mais l’application réelle ne les utilise pas) a deux contrôleurs de vue (nommés de manière non originale ViewController
et ViewController2
). ViewController
est contenu dans une UINavigationController
qui est le contrôleur de vue racine. ViewController
contient une UIWebView
(fonctionne correctement), un bouton qui «montre» («pousse») ViewController2
et une UIBarButtonItem
qui présente modalement ViewController2
. ViewController2
a une autre UIWebView
qui fonctionne quand “poussé” mais pas quand “présenté”.
ViewController
et ViewController2
sont chargés avec:
- (void)viewDidLoad {
[super viewDidLoad];
[self.webView loadHTMLString:@"<input type=\"file\" accept=\"image/*;capture=camera\">" baseURL:nil];
}
Lorsque vous essayez d'utiliser le modal UIWebView
Xcode imprime ce qui suit dans la console et rejette le modal d'application:
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 de UIActionSheet
à UIAlertController
pourraient avoir produit cette situation, mais il est assez difficile à prouver. Je vais ouvrir un radar avec Apple, juste au cas où.
Quelqu'un at-il trouvé la même situation et une solution de contournement?
J'ai trouvé que dans iOS 8.0.2, iPad ne semble pas avoir ce bogue, mais iPhone le reste.
Cependant, le suivi suivant dans le contrôleur de vue contenant le fichier uiwebview
-(void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion
Et vérifier qu’il existe un presentsViewController semble fonctionner.
Mais il faut 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' puis de remplacer cette méthode 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 bien pour moi
Pour moi, j'ai généralement un UINavigationController personnalisé afin que plusieurs vues puissent partager la même logique. Donc, pour ma solution de contournement (dans Swift), voici ce que j'ai mis dans mon NavigationController 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 des vues Web et qu'ils fonctionneront tous avec l'élément de téléchargement de fichier.
J'avais un problème similaire et j'ai découvert que les éléments UIWebView dans IOS ne prennent pas en charge 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 le laissera prendre/choisir une photo. CEPENDANT, UIWebView dans IOS ne permet pas de joindre de tels fichiers aux données POST lors de l'envoi du formulaire.
La solution: pour accomplir la même tâche, vous pouvez créer un formulaire similaire dans InterfaceBuilder avec un bouton déclenchant UIImagePickerController. Ensuite, vous créez une demande HTTP POST avec toutes les données du formulaire et l'image. Ce n'est pas aussi difficile que ça en a l'air, consultez le lien ci-dessous pour obtenir un exemple de code permettant d'accomplir le travail: ios Téléchargez une image et du texte via HTTP POST
Ce que j'ai fait et chaque fois que le contrôleur de vue change, convertissez la vue de destination à la racine.
depuis le premier contrôleur de 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 à la première, j'ai fait ceci.
depuis le contrôleur de vue Web:
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 sélectionner une photo de la bibliothèque, prendre une photo et tout le reste.
Je ne sais pas si c'est une bonne pratique, mais cela fonctionne très bien dans les émulateurs et les périphériques.
J'espère que ça aide.
Bon, voici la solution que j'ai finalement utilisée. C’est un changement de 2 minutes, joli hacky, mais fonctionne comme prévu et évite le bogue que nous avons tous. Fondamentalement, plutôt que de présenter le contrôleur de vue enfant de manière modale, je l'ai défini sur rootViewController de la fenêtre et j'ai gardé une référence sur le contrôleur parent. Donc, là où j'avais l'habitude d'avoir ceci (dans le contrôleur de vue parent):
presentViewController(newController, animated: true, completion: nil)
J'ai maintenant ceci
view.window?.rootViewController = newController
Dans les deux cas, le contrôleur parent est le délégué de newController
, ce qui permet de conserver la référence.
newController.delegate = self
Enfin, où dans mon rappel de délégué pour la fermeture du modal, où j'avais auparavant:
dismissViewControllerAnimated(true, completion: nil)
J'ai maintenant ceci:
viewController.view.window?.rootViewController = self
Nous obtenons donc le même effet de voir un contrôleur de vue s'emparer entièrement de l'écran, puis céder son contrôle quand c'est fait. Dans ce cas cependant, ce n'est pas animé. J'ai l'impression que l'animation de 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 modèle de délégué pour gérer la communication avec le contrôleur modal, conformément à la recommandation d'Apple, mais si vous n'êtes pas sûr, vous pouvez utiliser plusieurs méthodes qui conservent une référence et sont rappelées.
Ma solution consiste à créer un contrôleur de vue personnalisé avec une présentation modale personnalisée basée sur la hiérarchie parent-parent des contrôleurs. L'animation sera la même pour que l'utilisateur ne remarque 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
Elle ressemblera exactement à l’animation de présentation modale par défaut dans iOS7/8.