SwiftUI集成MoPub插屏广告时承载视图控制器Dismiss失效问题排查
MoPub插屏广告承载控制器dismiss异常问题
问题描述
在SwiftUI项目中通过自定义UIViewController承载MoPub插屏广告展示,除指定异常外所有功能运行正常:点击广告的关闭按钮后,广告自身可正常关闭,但广告后方作为承载层的自定义视图控制器(表现为黑屏)会一直驻留显示。
测试验证发现:手动调用的dismiss方法的完成回调可以正常触发打印,但承载广告的视图控制器本身并未从界面消失。
现存实现代码
class InterstitialAds: UIViewController, MPInterstitialAdControllerDelegate { var moPubView: MPInterstitialAdController? func viewControllerForPresentingModalView() -> UIViewController! { return self } //Called when you tap the ad's close button: func interstitialWillDismiss(_ interstitial: MPInterstitialAdController) { dismissControllerWithoutReward() } func showAd() { let topViewController = UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController self.modalPresentationStyle = .fullScreen topViewController?.present(self, animated: true) {} } func dismissControllerWithoutReward() { self.dismiss(animated: true) { print("dismissControllerWithoutReward()") //Successfully prints to console } } override func viewDidLoad() { let adId = "4f117153f5c24fa6a3a92b818a5eb630" //Test ad unit self.moPubView = MPInterstitialAdController(forAdUnitId: adId) if let v = self.moPubView { v.delegate = self v.loadAd() DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { v.show(from: self) } } super.viewDidLoad() } }
核心疑问
为什么dismiss方法已经被成功调用(完成回调可正常执行),承载广告的视图控制器却没有被正常dismiss?
临时验证现象
测试发现:在触发广告关闭代理方法后,延迟0.5s再调用dismiss方法,视图控制器就可以正常消失。目前已在interstitialWillDismiss(_:)代理方法中添加该延迟逻辑临时规避:
func interstitialWillDismiss(_ interstitial: MPInterstitialAdController) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { self.dismissControllerWithoutReward() } }
但仍需要明确异常的根本原因。
异常根本原因
- 实际的视图层级呈现结构为:应用根视图控制器 → 自定义的
InterstitialAds(全屏模态)→ MoPub插屏视图控制器(通过v.show(from: self)由MoPub SDK自动present在InterstitialAds之上) - 当用户点击广告关闭按钮时,MoPub SDK会先触发
interstitialWillDismiss代理回调,此时MoPub自身的dismiss转场动画才刚刚启动,UIKit的转场协调器正处于活跃占用状态 - 在代理回调中直接发起的
self.dismiss(animated:)请求,会因为和当前正在执行的MoPub内置转场任务抢占转场调度队列出现冲突:系统接收到dismiss调用后,直接触发了方法的完成回调,但没有将这个dismiss请求纳入实际的转场执行链路,因此不会真正将InterstitialAds从界面移除,最终留下黑屏残留。 - 延迟0.5s再调用dismiss可以生效的本质,是等待MoPub自身的dismiss转场完全执行完毕、系统转场协调器回到空闲状态后,新的dismiss请求可以被正常调度执行。
更优的无延迟实现方案:将dismiss逻辑从
interstitialWillDismiss方法迁移到interstitialDidDismiss代理方法中。该方法的触发时机为MoPub插屏的dismiss转场完全结束、系统转场回到空闲状态的节点,此时直接调用dismiss即可正常移除承载视图控制器,不需要额外添加硬编码等待延迟。
内容的提问来源于stack exchange,提问作者West1
相关产品推荐
相关产品推荐

