iOS 8 SDK: modal UIWebView and camera/image picker

喜夏-厌秋 提交于 2019-11-26 05:22:24

问题


I have found that, when compiling for iOS 8 (and running in iOS 8), a UIWebView cannot show the camera/image picker if the UIWebView is in a view controller presented modally. It works without problem in view controllers directly “hanging” from the window rootViewController or view controllers pushed from it.

The test application can be found at https://dl.dropboxusercontent.com/u/6214425/TestModalWebCamera.zip but I will describe it below.

My test application (built with storyboards, but the real application doesn’t use them) has two view controllers (unoriginally named ViewController and ViewController2). ViewController is contained in a UINavigationController which is the root view controller. ViewController contains a UIWebView (works OK), a button that “shows” (“pushes”) ViewController2, and a UIBarButtonItem which modally presents ViewController2. ViewController2 has another UIWebView which works when “pushed” but not when “presented”.

Both ViewController and ViewController2 are loaded with:

- (void)viewDidLoad {
  [super viewDidLoad];

  [self.webView loadHTMLString:@\"<input type=\\\"file\\\" accept=\\\"image/*;capture=camera\\\">\" baseURL:nil];
}

When trying to use the modal UIWebView Xcode prints the following in the console and dismisses the app modal:

Warning: Attempt to present <UIImagePickerController: 0x150ab800> on <ViewController2: 0x14623580> whose view is not in the window hierarchy!

My current theory is that the changes in UIActionSheet to UIAlertController might have produced this situation, but it’s quite hard to prove. I will open a Radar with Apple, just in case.

Has someone found the same situation and some workaround?


回答1:


I found that in iOS 8.0.2 iPad does not seem to have that bug but iPhone still does.

However, overriding following in the view controller containing the uiwebview

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

And checking that there is a presentedViewController seems to work.

But need to check side effects

#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



回答2:


I have the same issue on iOS 9. Try to add ivar '_flag' and then override this methods in view controller with 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];
}

This works for me fine




回答3:


For me I tend to have a custom UINavigationController so that multiple views can share the same logic. So for my workaround (in Swift) here is what I put in my custom NavigationController.

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
    }
}

This means that you can have loads of view controllers with webviews and they'll all work with the file upload element.




回答4:


I was having a similar issue, and I have discovered that the UIWebView elements in IOS do not support the html element:

I am not sure why Apple chose to not support this IMPORTANT html element, but I am sure they have their reasons. (Even though this element works perfectly on Safari on IOS.)

In many cases, when the user clicks this kind of button in a UIWebView, it will let them take/ choose a photo. HOWEVER, the UIWebView in IOS does not have the capability to attach files like this into the POST data when the form is submitted.

The Solution: To accomplish the same task you can create a similar form in InterfaceBuilder with a button that triggers the UIImagePickerController. Then, you create you an HTTP POST request with all of the form data and the image. It isn't as hard as it sounds, check out the link below for some sample code that gets the job done: ios Upload Image and Text using HTTP POST




回答5:


Alright, here's the workaround I ended up using. It's a 2 minute change, pretty hacky, but works as expected and avoids the bug we're all having. Basically, rather than presenting the child view controller modally, I set it to the window's rootViewController and keep a reference to the parent controller. So where I used to have this (in parent view controller):

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

I now have this

view.window?.rootViewController = newController

In both cases, the parent controller is newController's delegate, which is how I keep the reference.

newController.delegate = self

Finally, where in my delegate callback for closing the modal, where I used to have this:

dismissViewControllerAnimated(true, completion: nil)

I now have this:

viewController.view.window?.rootViewController = self

So we get the same effect of a view controller taking over the screen entirely, then yielding its control when it's done. In this case though, it's not animated. I have a feeling animating the controller transition wouldn't be very hard with some of the built in view animations.

Hopefully you're already using the delegate pattern to manage communication with the modal controller, per Apple's recommendation, but if you're not I'm sure you can use any number of methods that keep a reference and get called back.




回答6:


My solution is to create custom View Controller with custom modal presentation based on controllers child-parent hierarchy. Animation will be the same so the user will not notice the difference.

My suggestions for animation:

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

It will look exactly like default modal presentation animation in iOS7/8.




回答7:


What I did what every time the view controller is change, convert the destination view in root.

from first view controller:

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

view.window?.rootViewController = web

this is how I pass the root to the other, and when comeback to the first one I made this.

from web view controller:

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

and everything is fine, I can show the modal for select photo from library, take photo and everything else.

I don't know if that is a good practice but works fine in emulator and device.

Hope it helps.



来源:https://stackoverflow.com/questions/25942676/ios-8-sdk-modal-uiwebview-and-camera-image-picker

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!