web-dev-qa-db-ja.com

iOS 8 SDK:モーダルUIWebViewおよびカメラ/画像ピッカー

IOS 8用にコンパイルする(およびiOS 8で実行する)場合、UIWebViewがモーダルで表示されるView Controllerにある場合、UIWebViewはカメラ/画像ピッカーを表示できません。 View ControllerがウィンドウrootViewControllerから直接「ハング」する、またはそこからプッシュされたView Controllerで問題なく動作します。

テストアプリケーションは https://dl.dropboxusercontent.com/u/6214425/TestModalWebCamera.Zip にありますが、以下で説明します。

私のテストアプリケーション(ストーリーボードで構築されていますが、実際のアプリケーションでは使用されません)には、2つのView Controller(元はViewControllerViewController2という名前)があります。 ViewControllerは、ルートView ControllerであるUINavigationControllerに含まれています。 ViewControllerには、UIWebView(正常に動作します)、「表示」(「プッシュ」)ViewController2、およびViewController2をモーダル表示するUIBarButtonItemが含まれます。 ViewController2には別のUIWebViewがあり、これは「プッシュ」されたときに機能しますが、「提示された」ときは機能しません。

ViewControllerViewController2の両方がロードされます:

- (void)viewDidLoad {
  [super viewDidLoad];

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

モーダルUIWebViewを使用しようとすると、Xcodeはコンソールに次を出力し、アプリのモーダルを閉じます。

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

私の現在の理論では、UIActionSheetからUIAlertControllerへの変更がこの状況を生み出した可能性がありますが、証明するのは非常に困難です。念のために、Appleでレーダーを開きます。

誰かが同じ状況といくつかの回避策を見つけましたか?

35
yonosoytu

IOS 8.0.2では、iPadにはそのバグはないようですが、iPhoneにはまだあります。

ただし、uiwebviewを含むView Controllerで以下をオーバーライドする

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

また、presentedViewControllerがあることを確認することは機能しているようです。

しかし、副作用を確認する必要があります

#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
47
seeinvisible

IOS 9でも同じ問題があります。ivar '_flag'を追加してから、UIWebViewを使用してView Controllerでこのメソッドをオーバーライドしてください。

#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];
}

これは私のためにうまくいく

7
Artyom Devyatov

私にとっては、複数のビューが同じロジックを共有できるように、カスタムUINavigationControllerを使用する傾向があります。そのため、(Swiftでの)回避策として、カスタム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
    }
}

これは、WebViewを備えたView Controllerを多数ロードでき、それらはすべてファイルアップロード要素で動作することを意味します。

2
Jeremy JC Paul

私は同様の問題を抱えていましたが、IOSのUIWebView要素はhtml要素をサポートしていないことを発見しました:

なぜAppleはこの重要なhtml要素をサポートしないことを選択しましたが、その理由があると確信しています(この要素はIOSのSafariで完全に動作しますが)。

多くの場合、ユーザーがUIWebViewでこの種類のボタンをクリックすると、写真を撮影/選択できます。ただし、IOS=のUIWebViewには、フォームの送信時にこのようなファイルをPOSTデータに添付する機能がありません。

解決策:同じタスクを実行するには、UIImagePickerControllerをトリガーするボタンを使用して、InterfaceBuilderで同様のフォームを作成できます。次に、すべてのフォームデータと画像を使用してHTTP POSTリクエストを作成します。見た目ほど難しくありません。以下のリンクをチェックして、ジョブ完了: iosがHTTP POSTを使用して画像とテキストをアップロードする

1
Takide

View Controllerが変更されるたびに私が何をしたかは、ルートの宛先ビューを変換します。

最初のView Controllerから:

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

view.window?.rootViewController = web

これがルートを他に渡す方法であり、最初のルートに戻ったときにこれを作成しました。

web View Controllerから:

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

すべてが順調です。ライブラリから選択した写真のモーダルを表示したり、写真を撮ったりすることができます。

それが良い習慣であるかどうかはわかりませんが、エミュレータとデバイスでは問題なく動作します。

それが役に立てば幸い。

0
Jason Sa

さて、ここに私が使用してしまった回避策があります。これは2分間の変更で、かなりハッキングされますが、期待どおりに機能し、すべてのバグを回避します。基本的に、子View Controllerをモーダルで表示するのではなく、ウィンドウのrootViewControllerに設定し、親Controllerへの参照を保持します。だから私がこれを持っていた場所(親View Controller):

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

私は今これを持っています

view.window?.rootViewController = newController

どちらの場合も、親コントローラーはnewControllerのデリゲートです。これが、参照を保持する方法です。

newController.delegate = self

最後に、モーダルを閉じるためのデリゲートコールバックで、これを使用していた場所:

dismissViewControllerAnimated(true, completion: nil)

私は今これを持っています:

viewController.view.window?.rootViewController = self

したがって、View Controllerが画面を完全に引き継いで、完了時にそのコントロールを取得するのと同じ効果が得られます。ただし、この場合はアニメーション化されません。組み込みのビューアニメーションの一部では、コントローラーの遷移をアニメーション化するのはそれほど難しくないと感じています。

Appleの推奨に従って、既にデリゲートパターンを使用してモーダルコントローラーとの通信を管理していることを願っていますが、そうでない場合は、参照を保持してコールバックするメソッドをいくつでも使用できると確信しています。

0
bloudermilk

私の解決策は、コントローラーの子親階層に基づいたカスタムモーダルプレゼンテーションでカスタムView Controllerを作成することです。アニメーションは同じであるため、ユーザーは違いに気付かないでしょう。

アニメーションに関する私の提案:

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

IOS7/8のデフォルトのモーダルプレゼンテーションアニメーションとまったく同じように見えます。

0
kelin