问题描述
我有一个包含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()
仍然有点起伏,但大大减少了。