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?

35
demandé sur computingfreak 2014-09-20 01:34:10

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
45
répondu seeinvisible 2014-10-07 16:56:53

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

7
répondu Artyom Devyatov 2016-03-30 15:01:12

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

1
répondu Takide 2017-05-23 11:54:50

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.

1
répondu Jeremy JC Paul 2016-07-19 09:37:56

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é.

0
répondu bloudermilk 2014-09-30 17:10:49

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.

0
répondu kelin 2015-02-24 09:29:40

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.

0
répondu Jason Sa 2016-11-18 21:04:58