本文档基于 DemoCustomNativeAdapter
示例代码,详细介绍如何创建自定义原生广告适配器,实现第三方广告平台的原生广告接入。
Native/
├── DemoCustomNativeAdapter.h # 原生适配器头文件
├── DemoCustomNativeAdapter.m # 原生适配器实现文件
├── DemoCustomNativeDelegate.h # 原生代理头文件
├── DemoCustomNativeDelegate.m # 原生代理实现文件
├── DemoCustomNativeObject.h # 原生广告对象头文件
└── DemoCustomNativeObject.m # 原生广告对象实现文件
步骤:
ATCustomNetworkNativeAd
基类最终效果如下:
#import <Foundation/Foundation.h>
#import "DemoCustomAdapterCommonHeader.h"
@interface DemoCustomNativeObject : ATCustomNetworkNativeAd
@property (nonatomic, strong) MSNativeFeedAdModel *feedAdModel;
@property (nonatomic, strong) id<MSFeedAdMeta> feedAdMetaad;
@end
步骤:
- (void)registerClickableViews:(NSArray<UIView *> *)clickableViews withContainer:(UIView *)container registerArgument:(ATNativeRegisterArgument *)registerArgument {
if (self.nativeAdRenderType == ATNativeAdRenderExpress) {
//在这里处理第三方 SDK 所需要的模板广告需要的参数,赋值给第三方广告对象
[self templateRender];
return;
}
//在这里处理第三方 SDK 所需要的自渲染广告参数,通常是注册事件
[self slefRenderRenderClickableViews:clickableViews withContainer:container registerArgument:registerArgument];
}
#pragma mark - 模板
- (void)templateRender {
UIViewController *rootVC = self.configuration.rootViewController;
if (rootVC == nil) {
rootVC = [ATGeneralManage getCurrentViewControllerWithWindow:nil];
}
self.feedAdModel.presentVC = rootVC;
}
#pragma mark - 自渲染
- (void)slefRenderRenderClickableViews:(NSArray<UIView *> *)clickableViews withContainer:(UIView *)container registerArgument:(ATNativeRegisterArgument *)registerArgument {
UIViewController *rootVC = self.configuration.rootViewController;
if (rootVC == nil) {
rootVC = [ATGeneralManage getCurrentViewControllerWithWindow:nil];
}
[self.feedAdMetaad setMetaLogoFrame:self.configuration.logoViewFrame];
if ([self getMSFeedVideoView] && self.feedAdMetaad) {
[[self getMSFeedVideoView] registerDataObject:self.feedAdMetaad
clickableViews:clickableViews];
}
[self.feedAdMetaad attachAd:container renderViews:container.subviews clickView:clickableViews closeView:registerArgument.dislikeButton presentVc:rootVC];
}
- (void)setNativeADConfiguration:(ATNativeAdRenderConfig *)configuration {
//configuration对象是外界传入的配置对象,这里拿到后取出里面的属性设置给第三方 SDK。
self.configuration = configuration;
[self getMSFeedVideoView].presentVc = configuration.rootViewController;
}
- (void)dealloc {
[[self getMSFeedVideoView] unregisterDataObject];
[self.feedAdMetaad unAttachAd];
}
步骤:
#import <AnyThinkSDK.h>
@property (nonatomic,strong) ATNativeAdStatusBridge *adStatusBridge;
@property (nonatomic, strong) ATAdMediationArgument *adMediationArgument;
MSNativeFeedAdDelegate,MSFeedVideoDelegate
最终效果如下:
#import <Foundation/Foundation.h>
#import "DemoCustomAdapterCommonHeader.h"
@interface DemoCustomNativeDelegate : NSObject<MSNativeFeedAdDelegate,MSFeedVideoDelegate>
@property (nonatomic,strong) ATNativeAdStatusBridge *adStatusBridge;
@property (nonatomic, strong) ATAdMediationArgument *adMediationArgument;
@end
步骤:
/**
* 第三方的新原生广告加载广告数据成功回调,返回为广告物料的数组
*/
- (void)msNativeFeedAdLoaded:(MSNativeFeedAd *)nativeFeedAd feedAds:(NSArray <MSNativeFeedAdModel *> *)feedAds { //这是第三方 SDK 的协议方法,广告加载成功时调用
self.nativeFeedAd = nativeFeedAd;
[self nativeRenderingFeedAds:feedAds];
}
- (void)nativeRenderingFeedAds:(NSArray <MSNativeFeedAdModel *> *)feedAds {
MSNativeFeedAdModel *feedAdModel = feedAds.firstObject;
//判断是自渲染广告还是模版广告,走对应不同的处理逻辑
if (feedAdModel.isNativeExpress) {
[self _expressRenderingFeedAds:feedAds];
}else{
[self _selfRenderingFeedAds:feedAds];
}
}
/**
* 新原生广告加载广告数据失败回调
*/
- (void)msNativeFeedAdError:(MSNativeFeedAd *)nativeFeedAd withError:(NSError *)error {//这是第三方 SDK 的协议方法,广告加载失败时调用
//通知我们加载失败
[self.adStatusBridge atOnAdLoadFailed:error adExtra:nil];
}
- (void)_selfRenderingFeedAds:(NSArray <MSNativeFeedAdModel *> *)feedAds {
NSMutableArray *offerArray = [NSMutableArray array];
//如果需要实现客户端竞价,还需要获取第三方的广告价格
NSDictionary *infoDic = [DemoCustomBaseAdapter getC2SInfo:[feedAds.firstObject ecpm]];
[feedAds enumerateObjectsUsingBlock:^(MSNativeFeedAdModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
id<MSFeedAdMeta> ad = obj.adMaterialMeta;
//根据第三方的内容,创建我们 SDK 需要的原生广告对象
DemoCustomNativeObject *nativeObject = [[DemoCustomNativeObject alloc] init];
nativeObject.feedAdMetaad = ad;
nativeObject.nativeAdRenderType = ATNativeAdRenderSelfRender;
nativeObject.title = ad.metaContent;
nativeObject.mainText = ad.metaContent;
nativeObject.ctaText = ad.metaActionTitle;
nativeObject.rating = @([ad.metaAppScore doubleValue]);
nativeObject.appPrice = ad.metaAppPrice;
if (ad.metaCreativeType == MSCreativeTypeVideo) {
nativeObject.isVideoContents = YES;
}
nativeObject.videoDuration = ad.metaVideoDuration;
CGSize imageSize = ad.metaMainImageSize;
nativeObject.mainImageWidth = imageSize.width;
nativeObject.mainImageHeight = imageSize.height;
nativeObject.iconUrl = ad.metaIcon;
nativeObject.imageUrl = ad.metaImageUrls.firstObject;
nativeObject.imageList = ad.metaImageUrls;
// 视频广告相关处理,若第三方那边是含有视频的广告
if (ad.metaCreativeType == MSCreativeTypeVideo) {
nativeObject.isVideoContents = YES;
MSFeedVideoConfig *config = [MSFeedVideoConfig new];
config.isMute = [self.adMediationArgument.serverContentDic[@"video_muted"] intValue] == 0 ? NO : YES;;
config.isAutoPlay = NO;
MSFeedVideoView *feedVideoView = [[MSFeedVideoView alloc] init];
feedVideoView.videoConfig = config;
//重要:设置代理
feedVideoView.delegate = self;
//重要:将媒体视图赋值
nativeObject.mediaView = feedVideoView;
}
[offerArray addObject:nativeObject];
}];
//将组装好的对象,回传给我们 SDK
[self.adStatusBridge atOnNativeAdLoadedArray:offerArray adExtra:infoDic];
}
- (void)_expressRenderingFeedAds:(NSArray <MSNativeFeedAdModel *> *)feedAds {
NSMutableArray *offerArray = [NSMutableArray array];
//如果需要实现客户端竞价,还需要获取第三方的广告价格
NSDictionary *infoDic = [DemoCustomBaseAdapter getC2SInfo:[feedAds.firstObject ecpm]];
[feedAds enumerateObjectsUsingBlock:^(MSNativeFeedAdModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
UIView *nativeAdView = obj.feedView;
//根据第三方的内容,创建我们 SDK 需要的原生广告对象
DemoCustomNativeObject *nativeObject = [[DemoCustomNativeObject alloc] init];
nativeObject.feedAdModel = obj;
nativeObject.templateView = nativeAdView;
nativeObject.nativeAdRenderType = ATNativeAdRenderExpress;
nativeObject.nativeExpressAdViewWidth = nativeAdView.frame.size.width;
nativeObject.nativeExpressAdViewHeight = nativeAdView.frame.size.height;
nativeObject.isVideoContents = obj.isVideo;
[offerArray addObject:nativeObject];
}];
//将组装好的对象,回传给我们 SDK
[self.adStatusBridge atOnNativeAdLoadedArray:offerArray adExtra:infoDic];
}
ATNativeTrackProtocol
协议与ATBaseTrackProtocol
协议,实现其他广告事件并通知我们,例如:/**
* 广告被点击
*/
- (void)msNativeFeedAdClick:(MSNativeFeedAdModel *)feedAd {
[self.adStatusBridge atOnAdClick:nil];
}
/**
*注意⚠️:当该广告物料是⚠️[模版广告]⚠️时触发此回调
* 广告被关闭
* 详解:广告点击关闭后回调
*/
- (void)msNativeFeedAdClosed:(MSNativeFeedAdModel *)feedAd {
[self.adStatusBridge atOnAdClosed:nil];
}
/**
*注意⚠️:当该广告物料是⚠️自渲染广告⚠️时触发此回调
* 新原生广告点击以后,内置AppStore或是内置浏览器被关闭时回调
*/
- (void)msNativeFeedAdDetailClosed {
[self.adStatusBridge atOnAdDetailClosed:nil];
}
步骤:
DemoCustomBaseAdapter
,如果不清楚如何实现它,请参考[自定义基础适配器接入文档]章节。#import <AnyThinkSDK.h>
ATBaseNativeAdapterProtocol
协议步骤:
@interface DemoCustomNativeAdapter()
// 本文档2.1中实现的
@property (nonatomic, strong) DemoCustomNativeDelegate * nativeDelegate;
// 第三方SDK 的原生广告对象
@property (nonatomic, strong) MSNativeFeedAd * nativeExpressAd;
@end
nativeDelegate
属性#pragma mark - lazy
- (DemoCustomNativeDelegate *)nativeDelegate{
if (_nativeDelegate == nil) {
_nativeDelegate = [[DemoCustomNativeDelegate alloc] init];
_nativeDelegate.adStatusBridge = self.adStatusBridge;
}
return _nativeDelegate;
}
#pragma mark - init
- (void)loadADWithArgument:(ATAdMediationArgument *)argument {
dispatch_async(dispatch_get_main_queue(), ^{
//通过argument对象获取必要的加载信息,如尺寸等,创建好必要的参数,准备传入给第三方的原生加载方法,开始加载广告
self.nativeExpressAd = [[MSNativeFeedAd alloc]init];
//注意设置代理给DemoCustomNativeDelegate
self.nativeExpressAd.delegate = self.nativeDelegate;
self.nativeDelegate.adMediationArgument = argument;
MSNativeFeedAdConfigParams *adParam = [[MSNativeFeedAdConfigParams alloc]init];
adParam.adCount = [argument.at_serverDic[@"request_num"] intValue] ? [argument.at_serverDic[@"request_num"] intValue] : 1;
adParam.videoMuted = [argument.at_serverDic[@"video_muted"] intValue] == 0 ? NO : YES;;
adParam.isAutoPlay = NO;
if ([argument.localInfoDic[kATExtraInfoNativeAdSizeKey] respondsToSelector:@selector(CGSizeValue)]) {
CGSize size = [argument.localInfoDic[kATExtraInfoNativeAdSizeKey] CGSizeValue];
adParam.prerenderAdSize = size;
}
[self.nativeExpressAd loadAdWithPid:argument.serverContentDic[@"slot_id"] adParam:adParam];
});
}
#pragma mark - C2S Win Loss
- (void)didReceiveBidResult:(ATBidWinLossResult *)result { //接收我们 SDK 给的客户端竞价结果
if (result.bidResultType == ATBidWinLossResultTypeWin) {
[self sendWin:result];
return;
}
[self sendLoss:result];
}
- (void)sendWin:(ATBidWinLossResult *)result { //告诉第三方 SDK 竞价成功
NSMutableDictionary *infoDict = [DemoCustomBaseAdapter getWinInfoResult:result];
NSString *priceStr = [self.nativeExpressAd mediaExt][@"ecpm"];
[infoDict AT_setDictValue:priceStr key:kMSAdMediaWinPrice];
[self.nativeExpressAd sendWinNotificationWithInfo:infoDict];
}
- (void)sendLoss:(ATBidWinLossResult *)result { //告诉第三方 SDK 竞价失败
NSMutableDictionary *infoDic = [DemoCustomBaseAdapter getLossInfoResult:result];
[self.nativeExpressAd sendLossNotificationWithInfo:infoDic];
}
实现方式是在DemoCustomNativeObject.m
中实现ATAdLoadedNativeDataSource
中的协议方法,有关视频控制的内容如下:
@protocol ATAdLoadedNativeDataSource
#pragma mark - Video
/**
* The duration of the video ad playing, unit ms
*/
- (CGFloat)videoPlayTime;
/**
* Video ad duration, unit ms
*/
- (CGFloat)videoDuration;
/**
Play mute switch
@param flag whether to mute
*/
- (void)muteEnable:(BOOL)flag;
/**
* The video ad play
*/
- (void)videoPlay;
/**
* set voice button hidden, only suport TopOn Adx ad
*/
- (void)updateVoiceBtnHidden:(BOOL)hidden;
/**
* The video ad pause
*/
- (void)videoPause;
- (void)setVideoAutoPlay:(NSInteger)autoPlayType;
/// The destroy network native ad
- (void)destroyNative;
// 其他非视频相关
@optional
/// 广告物料格式
- (ATAdMaterialFormat)nativeMaterialFormat;
/// 原生广告类型
- (ATNativeAdType)nativeAdType;
/// CSJ recordCustomPlayerStatus
- (void)recordCustomPlayerStatus:(ATPlayerStatus)status currentTime:(NSTimeInterval)time;
@end
举例说明,例如我需要外部代码控制视频是否静音,则需要在DemoCustomNativeObject.m
中实现
@implementation DemoCustomNativeObject
- (void)muteEnable:(BOOL)flag {
//获取外部传入的是否静音标识,传入给第三方 SDK
}
......其他对接代码
@end
请前往广告事件回传接口列表。