iOS16下SwiftUI中AdMob插屏广告实现崩溃问题咨询
问题背景
iOS16环境下用SwiftUI开发AdMob插屏广告时会直接崩溃,iOS15上运行完全正常,崩溃报错如下:
SwiftUI/UIViewControllerRepresentable.swift:332: Fatal error:
UIViewControllerRepresentables must be value types: InterstitialAdView
2022-11-20 15:34:32.811071+0300 MyApp[38437:1156286]
SwiftUI/UIViewControllerRepresentable.swift:332: Fatal error:
UIViewControllerRepresentables must be value types: InterstitialAdView
核心冲突:UIViewControllerRepresentable要求实现者必须是值类型(struct),但NSObject和GADFullScreenContentDelegate只能在类(class)中实现,原代码用class同时实现三个协议,导致iOS16触发类型检查崩溃。
修改方案
把原有的单一class拆成两部分:一个struct负责实现UIViewControllerRepresentable,一个独立class负责处理AdMob的代理回调,既满足SwiftUI的类型要求,又能正常使用AdMob的代理功能。
1. 独立广告代理类
专门处理AdMob全屏广告的回调逻辑,继承NSObject并实现GADFullScreenContentDelegate:
final class InterstitialAdDelegate: NSObject, GADFullScreenContentDelegate { private let adUnitId: String private let onDismiss: () -> Void init(adUnitId: String, onDismiss: @escaping () -> Void) { self.adUnitId = adUnitId self.onDismiss = onDismiss super.init() } // 广告关闭后的回调 func adDidDismissFullScreenContent(_ ad: GADFullScreenPresentingAd) { // 加载下一条广告,为下次展示做准备 InterstitialAd.shared.loadAd(withAdUnitId: adUnitId) // 通知SwiftUI关闭广告视图 onDismiss() } }
2. SwiftUI广告视图struct
实现UIViewControllerRepresentable协议,负责创建视图控制器并展示广告:
struct InterstitialAdView: UIViewControllerRepresentable { @Binding var isPresented: Bool let adUnitId: String func makeUIViewController(context: Context) -> UIViewController { let viewController = UIViewController() // 延迟1毫秒展示,确保广告已加载完成 DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1)) { showAd(from: viewController) } return viewController } func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} private func showAd(from root: UIViewController) { guard let ad = InterstitialAd.shared.interstitialAd else { // 广告未加载成功,直接关闭视图 isPresented.toggle() return } // 创建代理实例,绑定关闭回调 let delegate = InterstitialAdDelegate(adUnitId: adUnitId) { [weak self] in self?.isPresented.toggle() } ad.fullScreenContentDelegate = delegate ad.present(fromRootViewController: root) } }
修改思路说明
- 拆分职责:把UIViewControllerRepresentable的实现(值类型要求)和AdMob代理逻辑(类类型要求)分开,规避类型冲突。
- 闭包传值:代理类通过闭包将广告关闭事件传递给SwiftUI视图,更新绑定的
isPresented状态。 - 避免内存泄漏:闭包中使用
[weak self]防止循环引用,确保视图销毁时相关资源能正常释放。
内容的提问来源于stack exchange,提问作者Aleksei

