ios 严重错误:隐式展开可选值时意外找到nil

zmeyuzjn  于 2023-03-05  发布在  iOS
关注(0)|答案(2)|浏览(128)

我想在外部屏幕的webview中执行javascript。在我的主视图中,我尝试调用外部视图中的pop()函数,如下所示:

let ex = ExternalDisplayViewController()
ex.pop(str: "Hello!")

当我运行它时,我得到"致命错误:在外部视图中的self.webView.evaluateJavaScript("go('\(str)')", completionHandler: nil)处隐式展开可选值"时意外发现nil:

import UIKit
import WebKit

class ExternalDisplayViewController: UIViewController, WKUIDelegate {
    
    private var webView: WKWebView!

    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
        
        let config = WKWebViewConfiguration()
        config.preferences.setValue(true, forKey: "allowFileAccessFromFileURLs")
        config.setValue(true, forKey: "allowUniversalAccessFromFileURLs")
        webView = WKWebView(frame: view.frame, configuration: config)
        webView.scrollView.contentInsetAdjustmentBehavior = .never
        webView.uiDelegate = self
        
        // load local html file
        let bundleURL = Bundle.main.resourceURL!.absoluteURL
        let html = bundleURL.appendingPathComponent("external.html")
        webView.loadFileURL(html, allowingReadAccessTo:bundleURL)
        view.addSubview(webView)
    }
    
    func pop(str: String) {
        self.webView.evaluateJavaScript("go('\(str)')", completionHandler: nil)
    }
}

谢谢!

编辑(2023年3月1日):

抱歉,我是Swift新手,使用多个视图。如果我使用viewDidLoad()loadView(),我会得到相同的结果:

import UIKit
import WebKit

class ExternalDisplayViewController: UIViewController, WKUIDelegate {

    private var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let config = WKWebViewConfiguration()
        config.preferences.setValue(true, forKey: "allowFileAccessFromFileURLs")
        config.setValue(true, forKey: "allowUniversalAccessFromFileURLs")
        webView = WKWebView(frame: view.frame, configuration: config)
        webView.scrollView.contentInsetAdjustmentBehavior = .never
        webView.uiDelegate = self

        // load local html file
        let bundleURL = Bundle.main.resourceURL!.absoluteURL
        let html = bundleURL.appendingPathComponent("external.html")
        webView.loadFileURL(html, allowingReadAccessTo:bundleURL)
        view.addSubview(webView)
    }

    func pop(str: String) {
        if (self.isViewLoaded) {
            // viewController is visible
            print("view controller should be visible")
            self.webView.evaluateJavaScript("go('\(str)')", completionHandler: nil)
        } else {
            print("view controller is not loaded")
            /*_ = self.view
            self.webView.evaluateJavaScript("go('\(str)')", completionHandler: nil)*/
        }
    }
}

这将生成以下输出:

View Loaded?
view controller is not loaded

如何初始化此视图以使其可访问?

qvtsj1bj

qvtsj1bj1#

当你写这行字的时候

let ex = ExternalDisplayViewController()

web视图将不会加载,您必须像这样将视图推送到navigationController

self.navigationController?.pushViewController(ex, animated: true)

之后你可以调用ex.pop(string:“str”),但您必须在调用此函数之前给它一点时间,直到视图加载,因此您可以在ExternalDisplayViewController中的函数viewDidLoad的末尾添加以下内容

NotificationCenter.default.post(name: NSNotification.Name(rawValue: "webViewDidLoad"), object: nil)

并且代替调用ex.pop(str:“Hello!”)改为编写以下代码

NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "webViewDidLoad"), object: nil, queue: nil) { _ in
        ex.pop(str: "str")
    }

但是要确保在调用self时viewController有一个navigationController。navigationController

50few1ms

50few1ms2#

我想在外部屏幕的webview中执行javascript。
我不是很清楚你所说的外部屏幕是什么意思,所以我假设你想用WKWebView初始化UIViewController,从你的包中加载external.html,并执行一个javascript函数,而不显示视图控制器。
(1)你应该保持对你的ExternalDisplayViewController的强引用,因为Swift的ARC可能会释放它。

class MainDisplayViewController: UIViewController {

    let ex = ExternalDisplayViewController() 

    func foo() {
        self.ex.pop(str: "Hello!") // This might not work, will explain more in (3).
    }
}

(2)UIViewController的生命周期函数,如viewDidLoadviewWillLayoutSubviews,在其视图加载到视图层次结构之前不会被调用。您可以从here了解更多关于生命周期的信息。
现在,在您的代码中,ExternalDisplayViewController中唯一要调用的函数是init函数,因此您必须将WKWebView初始化代码移到该函数中。

class ExternalDisplayViewController: UIViewController, WKUIDelegate {
  
    init() {
        ... // Remember to super.init first before calling self.
        self.setup()
    }

    func setup() {
        let config = WKWebViewConfiguration()
        ....
        self.view.addSubview(webView)
    }
}

(3)根据调用pop函数的位置和时间,你可能不能从webview调用你想要的javascript函数,因为在webview加载你的html和调用javascript函数之间会有一个竞态条件。你可以通过使用WKNavigationDelegate '来防止这种情况的didFinish委托函数,它会让你知道什么时候你的webview已经完成加载html。

class ExternalDisplayViewController: UIViewController, WKUIDelegate, WKNavigationDelegate {

    func setup() {
        ....
        self.webView.uiDelegate = self
        self.webView.navigationDelegate = self
        ....
    }

    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        self.webView.evaluateJavaScript("go('\(str)')", completionHandler: nil)
    }
}

(4)您还可以考虑将WKWebView代码移到主视图控制器中,因为您不打算展示ExternalDisplayViewController(假设我的假设是正确的)。

相关问题