iOS GAD横幅广告调用removeFromSuperview、isHidden无效不消失如何解决?
问题根因
- 横幅实例管理混乱:
setupBannerViewToTop方法每次执行都会重新创建GADBannerView实例覆盖原有变量,导致调用removeBannerView时只能移除最新的实例,旧的残留横幅无法被移除 - 激励广告回调后未全局更新:只有当前B控制器执行了移除横幅逻辑,导航栈中已存在的A控制器没有触发状态更新,横幅残留
- UserDefaults写入延迟:修改广告状态后未主动同步,
viewWillAppear读取时可能拿到旧值判断错误 - 移除逻辑不完善:仅调用
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
相关产品推荐
相关产品推荐

