IOS 8用にコンパイルする(およびiOS 8で実行する)場合、UIWebView
がモーダルで表示されるView Controllerにある場合、UIWebView
はカメラ/画像ピッカーを表示できません。 View ControllerがウィンドウrootViewController
から直接「ハング」する、またはそこからプッシュされたView Controllerで問題なく動作します。
テストアプリケーションは https://dl.dropboxusercontent.com/u/6214425/TestModalWebCamera.Zip にありますが、以下で説明します。
私のテストアプリケーション(ストーリーボードで構築されていますが、実際のアプリケーションでは使用されません)には、2つのView Controller(元はViewController
とViewController2
という名前)があります。 ViewController
は、ルートView ControllerであるUINavigationController
に含まれています。 ViewController
には、UIWebView
(正常に動作します)、「表示」(「プッシュ」)ViewController2
、およびViewController2
をモーダル表示するUIBarButtonItem
が含まれます。 ViewController2
には別のUIWebView
があり、これは「プッシュ」されたときに機能しますが、「提示された」ときは機能しません。
ViewController
とViewController2
の両方がロードされます:
- (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でレーダーを開きます。
誰かが同じ状況といくつかの回避策を見つけましたか?
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
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];
}
これは私のためにうまくいく
私にとっては、複数のビューが同じロジックを共有できるように、カスタム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を多数ロードでき、それらはすべてファイルアップロード要素で動作することを意味します。
私は同様の問題を抱えていましたが、IOSのUIWebView要素はhtml要素をサポートしていないことを発見しました:
なぜAppleはこの重要なhtml要素をサポートしないことを選択しましたが、その理由があると確信しています(この要素はIOSのSafariで完全に動作しますが)。
多くの場合、ユーザーがUIWebViewでこの種類のボタンをクリックすると、写真を撮影/選択できます。ただし、IOS=のUIWebViewには、フォームの送信時にこのようなファイルをPOSTデータに添付する機能がありません。
解決策:同じタスクを実行するには、UIImagePickerControllerをトリガーするボタンを使用して、InterfaceBuilderで同様のフォームを作成できます。次に、すべてのフォームデータと画像を使用してHTTP POSTリクエストを作成します。見た目ほど難しくありません。以下のリンクをチェックして、ジョブ完了: iosがHTTP POSTを使用して画像とテキストをアップロードする
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)
すべてが順調です。ライブラリから選択した写真のモーダルを表示したり、写真を撮ったりすることができます。
それが良い習慣であるかどうかはわかりませんが、エミュレータとデバイスでは問題なく動作します。
それが役に立てば幸い。
さて、ここに私が使用してしまった回避策があります。これは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の推奨に従って、既にデリゲートパターンを使用してモーダルコントローラーとの通信を管理していることを願っていますが、そうでない場合は、参照を保持してコールバックするメソッドをいくつでも使用できると確信しています。
私の解決策は、コントローラーの子親階層に基づいたカスタムモーダルプレゼンテーションでカスタムView Controllerを作成することです。アニメーションは同じであるため、ユーザーは違いに気付かないでしょう。
アニメーションに関する私の提案:
animateWithDuration:(animated ? 0.6 : 0.0)
delay:0.0
usingSpringWithDamping:1.f
initialSpringVelocity:0.6f
options:UIViewAnimationOptionCurveEaseOut
IOS7/8のデフォルトのモーダルプレゼンテーションアニメーションとまったく同じように見えます。