You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 14:53:26