加载广告管理器横幅广告时,CollectionView滚动不稳定

问题描述

我有一个包含n个部分和m行的集合视图。部分和行的数量是从API获取的。通常为18节,每节10行。每个单元格项目只有一个UIImageView 和两个UILabel

直到我在单元格中添加google ad manager banner ads为止,一切都可以顺利滚动。为此,我刚刚创建了一个新部分,该部分中的项目为1。并为单元格的大小提供横幅广告的全宽和相应高度。我按照Google广告管理器的UITableView示例在UICollectionView中实施。

但是现在无论何时通过UIRefreshControl加载或重新加载集合视图,都需要大约1-2秒的时间才能实现震撼的滚动行为。之后,滚动会顺利。

extension HomeVideosViewController {
    
    func loadBannerAd() {
        // call ad requests
        
        let filteredindices = self.homeVideosDatasource.datasource.indices.filter { (index) -> Bool in
            if self.homeVideosDatasource.datasource[index].section_type == "banner_ad" {
                return true
            }
            return false
        }
        
        for index in filteredindices {
            
            if let bannerAd = self.homeVideosDatasource.datasource[index].datasource.first as? BannerAd {
                
                // create banner ad object
                let bannerView: DFPBannerView!
                
                if bannerAd.is_mid_ad == true {
                    bannerView = DFPBannerView(adSize: kGADAdSizeLargeBanner)
                } else {
                    let frame = { () -> CGRect in
                        if #available(iOS 11.0,*) {
                            return self.view.frame.inset(by: self.view.safeAreaInsets)
                        } else {
                            return self.view.frame
                        }
                    }()
                    let viewWidth = frame.size.width
                    bannerView = DFPBannerView(adSize: GADCurrentOrientationAnchoredAdaptiveBannerAdSizeWithWidth(viewWidth))
                }
                
                // save index as tag for banner ads
                bannerView.tag = index
                bannerView.adUnitID = bannerAd.ad_code
                bannerView.rootViewController = self
                bannerView.delegate = self
                
                bannerView.load(DFPRequest())
                
                // replace bannerAd with banner view
                var updatedBannerAd = bannerAd
                updatedBannerAd.requestBannerView = bannerView
                self.homeVideosDatasource.datasource[index].datasource = [updatedBannerAd]
            }
        }
    }
    
    func addBannerViewToView(_ bannerView: GADBannerView) {
        dispatchQueue.main.async {
            let homeSection = self.homeVideosDatasource.datasource[bannerView.tag]
            
            // replace bannerAd with banner view
            if let bannerAd = homeSection.datasource.first as? BannerAd {
                
                var updatedBannerAd = bannerAd
                updatedBannerAd.receivedBannerView = bannerView
                updatedBannerAd.adRequestComplete = true
                homeSection.datasource = [updatedBannerAd]
                
                // replace home section with banner ads
                self.homeVideosDatasource.datasource[bannerView.tag] = homeSection
                
                // check if ads requests are complete
                self.checkForIncompleteAds()
            }
        }
    }
}

extension HomeVideosViewController: GADBannerViewDelegate {
    /// Tells the delegate an ad request loaded an ad.
    func adViewDidReceiveAd(_ bannerView: GADBannerView) {
        print("adViewDidReceiveAd for index : \(bannerView.tag)")
        
        dispatchQueue.global(qos: .background).async {
            self.addBannerViewToView(bannerView)
        }
    }

    /// Tells the delegate an ad request Failed.
    func adView(_ bannerView: GADBannerView,didFailToReceiveAdWithError error: GADRequestError) {
        print("adView:didFailToReceiveAdWithError: \(error.localizedDescription)")
        
            let homeSection = self.homeVideosDatasource.datasource[bannerView.tag]
            
            // replace bannerAd with banner view
            if let bannerAd = homeSection.datasource.first as? BannerAd {
                
                var updatedBannerAd = bannerAd
                updatedBannerAd.adRequestComplete = true
                homeSection.datasource = [updatedBannerAd]
                
                // replace home section with banner ads
                self.homeVideosDatasource.datasource[bannerView.tag] = homeSection
                
                // check if ads requests are complete
                self.checkForIncompleteAds()
            }
    }

    func checkForIncompleteAds() {

        let homeBannerAdsNoCompleted = self.homeVideosDatasource.datasource.filter { (homeVideosSection) -> Bool in
            if homeVideosSection.section_type == "banner_ad" {
                if let bannerAd = homeVideosSection.datasource.first as? BannerAd {
                    return !bannerAd.adRequestComplete
                }
                return false
            } else {
                return false
            }
        }
        
        if homeBannerAdsNoCompleted.count == 0 {
            // Reload CollectionView
            dispatchQueue.main.async {
                self.collectionView.reloadData()
            }
        }
    }
}

为缩小问题范围,我尝试评论将横幅广告放置在单元内的位置。但是震荡仍然存在。

然后,我尝试在addBannerViewToView的位置进行注释,以查看是否将广告放置在数据源中引起了问题,但是这种方法仍然不是问题。

接下来,我尝试评论checkForIncompleteAds(),但这也不是问题。

最可能的罪魁祸首是loadAds()。当我对此发表评论时,滚动很流畅。但是我不确定这可能是什么潜在的解决方案。我什至不能把它放在后台线程上。

感谢您的帮助。谢谢

解决方法

尝试加载广告视图后也重新加载collectionView,即self.collectionView.reloadData(),不仅在homeBannerAdsNoCompleted.count == 0之后

//Reload CollectionView
DispatchQueue.main.async {
    self.collectionView.reloadData()
}
,

因此问题出在请求广告(bannerView.load(DFPRequest()))上,这是在main线程上调用的,在我的代码中,此处以循环方式(目前为4)调用了{ {1}}线程。

Google建议您先调用第一个请求,然后再完成该请求,然后再调用第二个请求,而不是循环调用。那主线程上最多应该有一个活动请求。

所以我添加了两个属性:

main

var adsToLoad = [GADBannerView]() var loadStateForAds = [GADBannerView: Bool]() 保留要求的广告。 adsToLoad保持ad vs bool值知道哪些广告成功和哪些广告失败。

然后,该方法逐个请求广告。

loadStateForAds
将广告加载到 func preloadNextAd() { if !adsToLoad.isEmpty { let ad = adsToLoad.removeFirst() let adRequest = GADRequest() ad.load(adRequest) } } 时,首先调用

preloadNextAd()

adsToLoad

,然后从GAD委托方法。

                // load banner ads
                self.loadBannerAd()
                self.preloadNextAd()

仍然有点起伏,但大大减少了。