IOS13のモーダルビューコントローラーにWebViewがあります。ユーザーが画像をウェブビューにアップロードしようとすると、クラッシュします。
これは私が得ている例外です:
2019-09-30 17:50:10.676940 + 0900 Engage [988:157733]*キャッチされない例外 'NSGenericException'が原因でアプリを終了します、理由: 'アプリケーションがUIDocumentMenuViewController()を提示しました。現在のトレイト環境では、このスタイルのUIDocumentMenuViewControllerのmodalPresentationStyleはUIModalPresentationPopoverです。ビューコントローラーのpopoverPresentationControllerを介して、このポップオーバーの位置情報を提供する必要があります。 sourceViewおよびsourceRectまたはbarButtonItemのいずれかを提供する必要があります。ビューコントローラーを提示するときにこの情報がわからない場合は、UIPopoverPresentationControllerDelegateメソッド-prepareForPopoverPresentationで提供できます。 *まずスローコールスタック:(0x18926c98c 0x188f950a4 0x18cb898a8 0x18cb939b4 0x18cb914f8 0x18d283b98 0x18d2737c0 0x18d2a3594 0x1891e9c48 0x1891e4b34 0x1891e5100 0x1891e48bc 0x193050328 0x18d27a6d4 0x1002e6de4 0x18906f460)のlibc ++ abi.dylib:タイプNSExceptionのキャッチされない例外で終了
このデリゲートをどこに設定できるかわかりません...
サンプルプロジェクトを作成しました: https://github.com/ntnmrndn/WKUploadFormCrash そして、Appleへのバグレポートを記入しました
import UIKit
import WebKit
protocol WebViewTapGestureRecognizable {
var lastWebViewTapPosition: CGPoint { get }
}
extension WebViewTapGestureRecognizable where Self: UIViewController {
func prepareForPresent(_ viewControllerToPresent: UIViewController) {
if viewControllerToPresent is UIDocumentMenuViewController || viewControllerToPresent is UIDocumentPickerViewController,
UIDevice.current.userInterfaceIdiom == .phone,
let popoverPresentationController = viewControllerToPresent.popoverPresentationController {
popoverPresentationController.sourceView = view
let Origin = (self as WebViewTapGestureRecognizable).lastWebViewTapPosition
popoverPresentationController.sourceRect = CGRect(Origin: Origin, size: CGSize(width: 1, height: 1))
}
}
}
class WebView: WKWebView {
private(set) var lastWebViewTapPosition: CGPoint = CGPoint(x: 0, y: 0)
override init(frame: CGRect, configuration: WKWebViewConfiguration) {
super.init(frame: frame, configuration: configuration)
setupGestureRecognizer()
}
required init?(coder: NSCoder) { super.init(coder: coder) }
}
extension WebView: WebViewTapGestureRecognizable, UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
@objc func webViewTapped(_ sender: UITapGestureRecognizer) {
lastWebViewTapPosition = sender.location(in: superview ?? self)
}
private func setupGestureRecognizer() {
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(webViewTapped(_:)) )
tapGesture.delegate = self
addGestureRecognizer(tapGesture)
}
}
UIViewControllerの場合
import UIKit
class ViewController: UIViewController {
private weak var webView: WebView!
}
extension ViewController: WebViewTapGestureRecognizable {
var lastWebViewTapPosition: CGPoint { return webView.lastWebViewTapPosition }
override open func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
self.prepareForPresent(viewControllerToPresent)
super.present(viewControllerToPresent, animated: flag, completion: completion)
}
}
NavigationControllerの場合
import UIKit
class NavigationController: UINavigationController {}
extension NavigationController: WebViewTapGestureRecognizable {
var lastWebViewTapPosition: CGPoint { return (visibleViewController as? WebViewTapGestureRecognizable)?.lastWebViewTapPosition ?? .zero }
override open func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
self.prepareForPresent(viewControllerToPresent)
super.present(viewControllerToPresent, animated: flag, completion: completion)
}
}
ここにソリューションコードを貼り付けることを忘れないでください
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class RootViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
let stackView = UIStackView()
stackView.axis = .vertical
view.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
var button = UIButton()
button.setTitle("Present VC", for: .normal)
button.setTitleColor(.blue, for: .normal)
button.addTarget(self, action: #selector(buttonTouchedUpInside1), for: .touchUpInside)
stackView.addArrangedSubview(button)
button = UIButton()
button.setTitle("Present NavVC", for: .normal)
button.setTitleColor(.blue, for: .normal)
button.addTarget(self, action: #selector(buttonTouchedUpInside2), for: .touchUpInside)
stackView.addArrangedSubview(button)
}
@objc func buttonTouchedUpInside1() {
present(ViewController(), animated: true, completion: nil)
}
@objc func buttonTouchedUpInside2() {
present(NavigationController(rootViewController: ViewController()), animated: true, completion: nil)
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class ViewController: UIViewController {
private weak var webView: WebView!
override func viewDidLoad() {
super.viewDidLoad()
createWebView()
}
private func createWebView() {
let webView = WebView(frame: .zero,
configuration: WKWebViewConfiguration())
view.addSubview(webView)
self.webView = webView
//webView.navigationDelegate = self
webView.translatesAutoresizingMaskIntoConstraints = false
webView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
webView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
view.bottomAnchor.constraint(equalTo: webView.bottomAnchor).isActive = true
view.rightAnchor.constraint(equalTo: webView.rightAnchor).isActive = true
let urlString = "https://www.w3schools.com/tags/tryit.asp?filename=tryhtml5_input_type_file"
webView.load(URLRequest(url: URL(string: urlString)!))
}
}
extension ViewController: WebViewTapGestureRecognizable {
var lastWebViewTapPosition: CGPoint { return webView.lastWebViewTapPosition }
override open func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
self.prepareForPresent(viewControllerToPresent)
super.present(viewControllerToPresent, animated: flag, completion: completion)
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
import UIKit
class NavigationController: UINavigationController {}
extension NavigationController: WebViewTapGestureRecognizable {
var lastWebViewTapPosition: CGPoint { return (visibleViewController as? WebViewTapGestureRecognizable)?.lastWebViewTapPosition ?? .zero }
override open func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
self.prepareForPresent(viewControllerToPresent)
super.present(viewControllerToPresent, animated: flag, completion: completion)
}
}
私はここで解決策を見つけました: https://medium.com/swlh/popover-menu-over-cards-taining-webkit-views-on-ios-13-a16705aff8af 。著者に感謝します。
クラッシュを回避するために、これら2つの関数を追加するだけです。
override open func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
if #available(iOS 13, *), viewControllerToPresent is UIDocumentMenuViewController {
viewControllerToPresent.popoverPresentationController?.delegate = self
}
super.present(viewControllerToPresent, animated: flag, completion: completion)
}
func prepareForPopoverPresentation(_ popoverPresentationController: UIPopoverPresentationController) {
popoverPresentationController.sourceView = self.view
}
次に、ポップオーバーの位置を設定する必要があります。