iOS集成Admob的Native广告

Admob广告分为banner、插屏、激励和原生,其中原生广告的集成过程是最复杂的,并且完全按照官方文档或者官方的示例程序是无法成功的,所以有必要把我的集成方法分享给大家。

到公众号【iOS开发栈】学习更多SwiftUI、iOS开发相关内容。

以下内容基于Google-Mobile-Ads-SDK的8.9.0版本。如果不适用于你的版本可以联系我,一块讨论解决。

Admob原生广告

原生广告是一种可以自定义广告样式的广告形式,通过集成原生广告可以根据自己App的已有UI来选择广告的样式,使广告能够跟App尽量融合在一起,减少割裂感,进而减少广告对用户的打扰。这种自定义外观的功能是其他形式的广告所不具备的。

想要集成原生广告总体来说需要进行两步操作:一是自定义广告的界面,二是通过SDK获取广告的数据。

获取原生广告数据

1
2
3
4
5
6
let nativeAdOptions = GADNativeAdViewAdOptions()
nativeAdOptions.preferredAdChoicesPosition = .bottomLeftCorner;
let adLoader = GADAdLoader(adUnitID: "ca-app-pub-3940256099942544/3986624511", rootViewController: self, adTypes: [.native],
options: [nativeAdOptions])
adLoader.delegate = self
adLoader.load(GADRequest())

原生广告加载回调

GADAdLoader对象的delegate需要遵守GADNativeAdLoaderDelegate代理,这个代理遵守了GADAdLoaderDelegate代理,因此这两个代理中的回调都可以用。其中:

GADAdLoaderDelegate代理中的adLoader:didReceiveNativeAd:表示原生广告加载完成。

GADAdLoaderDelegate中的adLoader:didFailToReceiveAdWithError:表示广告加载失败。

在Admob的文档中提到尽量不要在加载失败的回调中重试,不过我认为这里指的是不要立即重试,在实际开发过程中可以根据业务需求采取间隔一定时间后重试。

在成功的回调中需要把界面和获取到的数据进行绑定,下面有详细的步骤。

自定义原生广告样式

根据Admob文档的描述,可以通过xib的形式来自定义原生广告样式。可以通过查看GADNativeAdView类确定原生广告支持的组件类型和个数,里面大多数都是用的UIView不过在实际过程中需要区分UILabel/UIImageView或者UIButton

实际类型 字段名
UILabel headlineView bodyView storeView priceView advertiserView
UIButton callToActionView
UIImageView iconView imageView starRatingView
GADMediaView mediaView

根据UI需求完成xib的布局:

并把这个xib文件命名为:AdmobNative.xib。

截止到这里你会发现我们并没有创建类文件,那是因为Admob的SDK里已经有这样的类文件了,我们需要做的就是把这个xib文件和已经存在的类文件关联起来,也就是“连上线”。

xib文件关联类

如果按照官方文档的描述,可以直接把刚刚创建的xib文件和Admob库里面GADNativeAd.h文件中的GADNativeAdView类进行关联,而这个类中属性也都是IBOutlet的,但是当你这么做的时候发现并不能连上线,无法进行关联。

之所以出现这种情况是因为官方文档过时了,Google-Mobile-Ads-SDK库在之前是framework的形式,而现在改成了xcframework

所以要想进行连线就得另辟蹊径了。

我们都知道,OC的.h和.m文件类似C语言的.h和.m,具体来说就是.h文件只是接口声明,它的作用就是让编译器不报错,而在实际运行时还是要看.m文件中是否确实有该方法或者变量。

作为仅有声明作用的.h文件,我们是可以随便创建的,只要声明的内容在.m文件里有实现就可以了。那么我们就可以Google-Mobile-Ads-SDKGADNativeAd.h文件拷贝一份到自己的项目里

GADNativeAd.h文件原封不动的拷贝到自己的项目中后,就可以随心所欲的进行连线了。

把xib中的界面和类文件里的属性关联以后,就可以把请求到的广告内容显示到界面上了。

显示获取到的广告数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// Mark: - GADNativeAdLoaderDelegate
func adLoader(_ adLoader: GADAdLoader, didReceive nativeAd: GADNativeAd) {
print("Received native ad: \(nativeAd)")
refreshAdButton.isEnabled = true
// Create and place ad in view hierarchy.
let nibView = Bundle.main.loadNibNamed("NativeAdView", owner: nil, options: nil)?.first
guard let nativeAdView = nibView as? GADNativeAdView else {
return
}
setAdView(nativeAdView)

// Set ourselves as the native ad delegate to be notified of native ad events.
nativeAd.delegate = self

// Populate the native ad view with the native ad assets.
// The headline and mediaContent are guaranteed to be present in every native ad.
(nativeAdView.headlineView as? UILabel)?.text = nativeAd.headline
nativeAdView.mediaView?.mediaContent = nativeAd.mediaContent

// This app uses a fixed width for the GADMediaView and changes its height to match the aspect
// ratio of the media it displays.
if let mediaView = nativeAdView.mediaView, nativeAd.mediaContent.aspectRatio > 0 {
let heightConstraint = NSLayoutConstraint(
item: mediaView,
attribute: .height,
relatedBy: .equal,
toItem: mediaView,
attribute: .width,
multiplier: CGFloat(1 / nativeAd.mediaContent.aspectRatio),
constant: 0)
heightConstraint.isActive = true
}

// These assets are not guaranteed to be present. Check that they are before
// showing or hiding them.
(nativeAdView.bodyView as? UILabel)?.text = nativeAd.body
nativeAdView.bodyView?.isHidden = nativeAd.body == nil

(nativeAdView.callToActionView as? UIButton)?.setTitle(nativeAd.callToAction, for: .normal)
nativeAdView.callToActionView?.isHidden = nativeAd.callToAction == nil

(nativeAdView.iconView as? UIImageView)?.image = nativeAd.icon?.image
nativeAdView.iconView?.isHidden = nativeAd.icon == nil

(nativeAdView.starRatingView as? UIImageView)?.image = imageOfStars(
fromStarRating: nativeAd.starRating)
nativeAdView.starRatingView?.isHidden = nativeAd.starRating == nil

(nativeAdView.storeView as? UILabel)?.text = nativeAd.store
nativeAdView.storeView?.isHidden = nativeAd.store == nil

(nativeAdView.priceView as? UILabel)?.text = nativeAd.price
nativeAdView.priceView?.isHidden = nativeAd.price == nil

(nativeAdView.advertiserView as? UILabel)?.text = nativeAd.advertiser
nativeAdView.advertiserView?.isHidden = nativeAd.advertiser == nil

// In order for the SDK to process touch events properly, user interaction should be disabled.
nativeAdView.callToActionView?.isUserInteractionEnabled = false

// Associate the native ad view with the native ad object. This is
// required to make the ad clickable.
// Note: this should always be done after populating the ad views.
nativeAdView.nativeAd = nativeAd
}

其中有两点需要注意:一是通过设置nativeAdView.callToActionView?.isUserInteractionEnabled = false来使用户可以通过点击按钮跳转到App Store或者广告主的网站,二是nativeAdView.nativeAd = nativeAd来把数据和界面进行关联,只有这样广告的元素才会响应用户点击执行跳转。

广告视图事件处理

上面的nativeAd.delegate = self可以让当前类获取到广告视图的事件回调,比如点击、跳转、展示等,具体可以查看GADNativeAdDelegate。其中有一点需要注意的是nativeAdDidRecordImpression:nativeAdDidRecordClick:回调仅对Admob自家的广告有效,也就是说如果通过Admob聚合其他广告商的原生广告是无法获取到这两个回调事件的。

至此,集成Admob原生广告的工作就结束了。👍

到公众号【iOS开发栈】学习更多SwiftUI、iOS开发相关内容。

总结

通过这篇文章我们对Admob原生广告的集成过程有了一个全面的了解,并且对一些官方文档的过时内容也进行了更正处理,相信你现在可以处理接入Admob原生广告的需求了,那么动手开始吧。