J'ai une vue Web dans un contrôleur de vue modale sur iOS13. Lorsque l'utilisateur essaie de télécharger une image sur la vue Web, il se bloque.
C'est l'exception que je reçois:
2019-09-30 17: 50: 10.676940 + 0900 Engage [988: 157733] * Arrêt de l'application en raison d'une exception non interceptée 'NSGenericException', raison: 'Votre application a présenté un UIDocumentMenuViewController (). Dans son environnement de traits actuel, le modalPresentationStyle d'un UIDocumentMenuViewController avec ce style est UIModalPresentationPopover. Vous devez fournir des informations d'emplacement pour ce popover via le popoverPresentationController du contrôleur de vue. Vous devez fournir un sourceView et un sourceRect ou un barButtonItem. Si ces informations ne sont pas connues lorsque vous présentez le contrôleur de vue, vous pouvez les fournir dans la méthode UIPopoverPresentationControllerDelegate -prepareForPopoverPresentation. ' * Première pile d'appel de touche: (0x18926c98c 0x188f950a4 0x18cb898a8 0x18cb939b4 0x18cb914f8 0x18d283b98 0x18d2737c0 0x18d2a3594 0x1891e9c48 0x1891e4b34 0x1891e5100 0x1891e48bc 0x193050328 0x18d27a6d4 0x1002e6de4 0x18906f460) libc ++ abi.dylib: mettre fin à l'exception non interceptée du genre NSException
Je ne sais pas où pourrais-je placer ce délégué ...
J'ai fait un exemple de projet: https://github.com/ntnmrndn/WKUploadFormCrash Et rempli un rapport de bogue à 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)
}
}
Pour 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)
}
}
Pour 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)
}
}
N'oubliez pas de coller ici le code de la solution
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
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)
}
}
J'ai trouvé la solution ici: https://medium.com/swlh/popover-menu-over-cards-containing-webkit-views-on-ios-13-a16705aff8af . Merci à l'auteur.
Je viens d'ajouter ces deux fonctions pour éviter le plantage.
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
}
Ensuite, vous devez définir la position de votre popover.