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

iOS GAD横幅广告调用removeFromSuperview、isHidden无效不消失如何解决?

问题根因
  1. 横幅实例管理混乱:setupBannerViewToTop 方法每次执行都会重新创建 GADBannerView 实例覆盖原有变量,导致调用 removeBannerView 时只能移除最新的实例,旧的残留横幅无法被移除
  2. 激励广告回调后未全局更新:只有当前B控制器执行了移除横幅逻辑,导航栈中已存在的A控制器没有触发状态更新,横幅残留
  3. UserDefaults写入延迟:修改广告状态后未主动同步,viewWillAppear 读取时可能拿到旧值判断错误
  4. 移除逻辑不完善:仅调用 removeFromSuperview 未做兜底隐藏,特殊场景下系统渲染可能残留广告层

修复方案

1. 重构BaseViewController横幅管理逻辑

统一管理横幅实例,完善添加/移除的兜底逻辑

public class BaseViewController: UIViewController {
  // 全局复用单个横幅实例,避免重复创建
  public lazy var bannerView: GADBannerView = {
    let adSize = GADAdSizeFromCGSize(CGSize(width: UIScreen.main.bounds.width, height: 50))
    let banner = GADBannerView(adSize: adSize)
    banner.translatesAutoresizingMaskIntoConstraints = false
    banner.adUnitID = Constants.GoogleAds.testUnitId
    banner.delegate = self
    return banner
  }()
  
  let ud = UserDefaults.standard
  
  func confirmRemainFreeAdsTime() {
    let current = Date()
    let reactiveBannerTime = ud.object(forKey: "reactiveBannerTime") as? Date
    if let diffTime = reactiveBannerTime?.timeIntervalSince(current), diffTime <= 0 {
      ud.set(false, forKey: "removeAds24hours")
      ud.synchronize() // 主动同步避免读取延迟
    }
  }
  
  // 统一横幅状态更新入口,所有页面共用避免重复逻辑
  @objc func updateBannerStatus() {
    confirmRemainFreeAdsTime()
    let shouldHideBanner = ud.bool(forKey: "removeAds24hours")
    shouldHideBanner ? removeBannerView() : setupBannerViewToTop()
  }
}

extension BaseViewController: GADBannerViewDelegate {
  func setupBannerViewToTop(height: CGFloat = 50) {
    // 先移除旧实例避免重复添加
    if bannerView.superview != nil {
      bannerView.removeFromSuperview()
    }
    bannerView.rootViewController = self
    // 更新高度约束
    bannerView.constraints.forEach { cons in
      if cons.firstAttribute == .height {
        cons.constant = height
      }
    }
    
    view.addSubview(bannerView)
    NSLayoutConstraint.activate([
      bannerView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
      bannerView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
      bannerView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
      bannerView.heightAnchor.constraint(equalToConstant: height)
    ])
    bannerView.isHidden = false
    bannerView.load(GADRequest())
  }
  
  func removeBannerView() {
    // 双重保险,先隐藏再移除
    bannerView.isHidden = true
    if bannerView.superview != nil {
      bannerView.removeFromSuperview()
    }
    bannerView.rootViewController = nil // 解绑避免后续自动渲染
  }
}

2. 简化A、B控制器生命周期逻辑

所有展示横幅的页面统一调用封装好的状态更新方法

// A控制器
override func viewWillAppear(_ animated: Bool) {
  super.viewWillAppear(animated)
  updateBannerStatus()
}

override func viewWillDisappear(_ animated: Bool) {
  super.viewWillDisappear(animated)
  removeBannerView()
}

// B控制器
override func viewWillAppear(_ animated: Bool) {
  super.viewWillAppear(animated)
  updateBannerStatus()
  // 仅保留B独有的按钮文案更新逻辑
  let removeBanner = ud.bool(forKey: "removeAds24hours")
  if removeBanner {
    let dateString = ud.string(forKey: "dateString") ?? ""
    let nobannerUntil = NSLocalizedString("noBannerUntil", comment: "")
    self.removeAdsBtn.setTitle("\(nobannerUntil) \(dateString)", for: .normal)
    self.removeAdsBtn.setTitleColor(UIColor(named: "MiddleYellow") ?? .black, for: .normal)
  }
}

override func viewWillDisappear(_ animated: Bool) {
  super.viewWillDisappear(animated)
  removeBannerView()
}

3. 优化激励广告完成后的更新逻辑

回调切主线程操作UI,同时更新导航栈所有页面的横幅状态

GADRewardedAd.load(withAdUnitID: Constants.GoogleAds.rewardTestUnitId, request: GADRequest()) { rewardedAd, error in
  if let error = error {
    print("Failed to load rewarded ad with error: \(error.localizedDescription)")
    return
  }
  self.rewardedAdGlobal = rewardedAd
  self.rewardedAdGlobal?.fullScreenContentDelegate = self
  guard let ad = self.rewardedAdGlobal else { return }
  
  ad.present(fromRootViewController: self) {
    // 所有UI操作切主线程执行
    DispatchQueue.main.async {
      let reactiveBannerDate = Date(timeIntervalSinceNow: 60 * 1)
      self.ud.set(reactiveBannerDate, forKey: "reactiveBannerTime")
      let formatter = DateFormatter()
      formatter.timeZone = TimeZone.current
      formatter.dateFormat = "[ yyyy-MM-dd a hh:mm ]"
      let dateString = formatter.string(from: reactiveBannerDate)
      self.ud.set(dateString, forKey: "dateString")
      self.ud.set(true, forKey: "removeAds24hours")
      self.ud.synchronize() // 主动同步配置
      
      // 遍历导航栈所有页面更新横幅状态
      if let navVC = self.navigationController {
        navVC.viewControllers.forEach { vc in
          if let baseVC = vc as? BaseViewController {
            baseVC.updateBannerStatus()
          }
        }
      }
    }
  }
}

内容的提问来源于stack exchange,提问作者markelopsigner

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 06:24:03