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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.22 22:54:09