Swift调用ExternalDisplayViewController的pop方法时WKWebView解包nil崩溃及视图初始化问题
解决ExternalDisplayViewController中WKWebView为nil的问题
问题根源
你直接实例化ExternalDisplayViewController后,没有将它的视图添加到外部屏幕的窗口中,导致VC的viewDidLoad方法从未执行,webView始终处于未初始化的nil状态,调用pop时就会触发隐式解包错误。
解决方案
1. 正确处理外部屏幕的连接与窗口创建
需要监听外部屏幕的连接事件,为外部屏幕创建独立的窗口,并将ExternalDisplayViewController作为该窗口的根视图控制器。
2. 确保VC视图加载后再调用pop方法
只有当VC的视图被添加到窗口并完成加载后,webView才会被初始化,此时调用pop才安全。
修改后的完整代码示例
主视图控制器中处理外部屏幕
import UIKit class MainViewController: UIViewController { private var externalWindow: UIWindow? private var externalVC: ExternalDisplayViewController? override func viewDidLoad() { super.viewDidLoad() // 监听外部屏幕连接事件 NotificationCenter.default.addObserver(self, selector: #selector(handleScreenConnect(notification:)), name: UIScreen.didConnectNotification, object: nil) // 监听外部屏幕断开事件 NotificationCenter.default.addObserver(self, selector: #selector(handleScreenDisconnect(notification:)), name: UIScreen.didDisconnectNotification, object: nil) } @objc private func handleScreenConnect(notification: Notification) { guard let externalScreen = notification.object as? UIScreen else { return } // 创建外部窗口 externalWindow = UIWindow(frame: externalScreen.bounds) externalWindow?.screen = externalScreen // 实例化外部VC并设置为根控制器 externalVC = ExternalDisplayViewController() externalWindow?.rootViewController = externalVC // 显示窗口,触发VC的viewDidLoad externalWindow?.isHidden = false } @objc private func handleScreenDisconnect(notification: Notification) { externalWindow = nil externalVC = nil } // 调用外部VC的pop方法(示例按钮点击事件) @IBAction func triggerPop(_ sender: UIButton) { externalVC?.pop(str: "Hello!") } }
修改后的ExternalDisplayViewController
import UIKit import WebKit class ExternalDisplayViewController: UIViewController, WKUIDelegate { // 改为可选类型,避免隐式解包风险 private var webView: WKWebView? override func viewDidLoad() { super.viewDidLoad() setupWebView() } private func setupWebView() { let config = WKWebViewConfiguration() config.preferences.setValue(true, forKey: "allowFileAccessFromFileURLs") config.setValue(true, forKey: "allowUniversalAccessFromFileURLs") webView = WKWebView(frame: view.bounds, configuration: config) guard let webView = webView else { return } webView.scrollView.contentInsetAdjustmentBehavior = .never webView.uiDelegate = self webView.autoresizingMask = [.flexibleWidth, .flexibleHeight] // 添加自动布局适配 // 加载本地HTML文件 guard let bundleURL = Bundle.main.resourceURL else { return } let htmlURL = bundleURL.appendingPathComponent("external.html") webView.loadFileURL(htmlURL, allowingReadAccessTo: bundleURL) view.addSubview(webView) } func pop(str: String) { // 安全调用,避免nil崩溃 guard let webView = webView, isViewLoaded else { print("WebView未初始化或视图未加载") return } // 注意转义字符串中的特殊字符,避免JS语法错误 let escapedStr = str.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? str webView.evaluateJavaScript("go('\(escapedStr)')") { result, error in if let error = error { print("JS执行错误: \(error.localizedDescription)") } } } }
关键优化点
- 将
webView改为可选类型,配合guard语句做安全校验,彻底避免隐式解包的崩溃风险 - 为
webView添加autoresizingMask,确保外部屏幕尺寸变化时webView能自适应 - 对传入的字符串做转义处理,防止包含单引号等字符导致JS语法错误
- 通过系统通知正确监听外部屏幕的连接/断开,确保VC的视图被正确加载到外部窗口中
内容的提问来源于stack exchange,提问作者Josiah
相关产品推荐
相关产品推荐

