原生广告(模版\自渲染\draw)
参考Demo 原生广告分为自渲染广告和模板广告,但是模板广告只有当三方SDK支持时才会返回模板广告。SDK 提供信息流广告的数据绑定、点击事件的上报
- 自渲染广告:聚合SDK返回物料,由开发者在返回的WindMillNativeAdView类型的view上进行子视图的自行渲染和展示。
- 模板广告:聚合SDK直接返回渲染好的广告WindMillNativeAdView,开发者直接展示即可。
- isAdReady方法430版本开始 由WindMillNativeAdsManager 转移到 WindMillNativeAd 对象下,针对每条播放的广告进行判断。
1. 原生广告管理类WindMillNativeAdsManager
在SDK里只需要使用 WindMillNativeAdsManager 就可以获取信息流广告。WindMillNativeAdsManager支持多广告加载,可以一次加载返回多个广告。
注意:adSize高度为0时,模版渲染render success后自动更新到合适的高度
@interface WindMillNativeAdsManager : NSObject
@property (nonatomic, weak) id<WindMillNativeAdsManagerDelegate> delegate;
@property (nonatomic, strong, readonly) NSString *placementId;
/// 自定义广告扩展参数
@property (nonatomic, strong, nullable) NSDictionary *extra;
/**
* 必传参数,为空时聚合会默认设置为width=screen.bouces.size.width; height=0
* 穿山甲 height: 0自适应
* 腾讯:height会在render success后自动更新到合适的高度
*/
@property (nonatomic, assign, readwrite) CGSize adSize;
- (instancetype)initWithRequest:(WindMillAdRequest *)request;
/**
It is recommended to request no more than 3 ads.
*/
- (void)loadAdDataWithCount:(NSInteger)count;
/**
Get all ads when call methord: nativeAdsManagerSuccessToLoad
*/
- (NSArray<WindMillNativeAd *> *)getAllNativeAds;
/**
填充后可调用, 返回广告缓存池内所有信息
*/
- (NSArray<WindMillAdInfo *> *)getCacheAdInfoList;
/// 自定义分组规则支持单次广告load维度设置
- (void)setLoadCustomGroup:(NSDictionary<NSString *, NSString *> *)customGroupRules;
// 广告对象级广告源过滤器
- (void)addFilter:(WindMillAdFilter *)filter;
// 移除广告源过滤器
- (void)removeFilter;
@end2. 原生广告类WindMillNativeAdView
WindMillNativeAdView:为请求原生广告返回的广告模型后,创建WindMillNativeAdView后,使用refreshData:渲染
- 自渲染广告(feedADMode != WindMillFeedADModeNativeExpress) 开发者根据WindMillNativeAdView提供的组件和WindMillNativeAd提供的物料信息对WindMillNativeAdView进行布局。
- 模板广告(feedADMode = WindMillFeedADModeNativeExpress) 开发者可直接将ABUNativeAdView添加到父视图上进行展示。
注意:目前布局方式支持:frame布局方式 和 自动布局方式
@interface WindMillNativeAdView : UIView
/// 广告数据对象
@property (nonatomic, strong, readonly) WindMillNativeAd *nativeAd;
/// 当前播放广告的广告信息
@property (nonatomic, strong, readonly) WindMillAdInfo *adInfo;
/// 广告代理对象
@property (nonatomic, weak) id<WindMillNativeAdViewDelegate> delegate;
/// 广告点击弹出新页面所依赖的ViewController
@property (nonatomic, weak) UIViewController *viewController;
/// 视频组件
@property (nonatomic, strong, readonly) UIView *mediaView;
/// 广告平台logo组件
@property (nonatomic, strong, readonly) UIView *logoView;
/// dislike组件
@property (nonatomic, strong, readonly) UIButton *dislikeButton;
/// 单图广告对应的图片组件
@property (nonatomic, strong, readonly) UIImageView *mainImageView;
/// 摇一摇组件 [Optional] [仅支持百度]
/// 因为是懒加载方式,需要在refreshData之后确认渲染渠道后才会创建对应的摇一摇组件
/// 组件高度需要大于80pt。摇一摇文案内容为:摇动手机 了解更多 如果组件宽度不够,则会自动将宽度适配文字长度。
@property (nonatomic, strong, readonly) WindMillNativeAdShakeView *shakeView;
/// 互动组件 [410版本开始仅支持sigmob渠道]
@property (nonatomic, strong, readonly) UIView *interactiveView;
/// 3图广告时对应的ImageView组件list
@property (nonatomic, strong, readonly) NSArray<UIImageView *> *imageViewList;
/// 刷新广告数据
/// @param nativeAd 广告模型数据
/// @warning 每次获取物料信息后需要刷新调用refreshData:方法
- (void)refreshData:(WindMillNativeAd *)nativeAd;
/**
刷新media视图frame,百度渠道
*/
- (void)mediaViewResize;
/**
设置media视图frame,需要在refreshData之后调用,美数渠道
*/
- (void)setMediaViewSize:(CGSize)size;
/// 绑定点击的View
/// @param clickableViews 需要注册点击View的集合
- (void)setClickableViews:(NSArray<UIView *> *)clickableViews;
/// 设置占位图
/// @param placeholderImage 占位图UIImage *
- (void)setPlaceholderImage:(UIImage *)placeholderImage;
/// 注销数据对象
/// @warning 在 tableView、collectionView 等场景需要复用 WindNativeAdView 时,需要在合适的时机,例如 cell 的 prepareForReuse 方法内执行 unregisterDataObject 方法,将广告对象与 WindNativeAdView 解绑
- (void)unregisterDataObject;
@end3. 自渲染广告物料信息
- 在物料加载成功方法里获取相关广告信息赋值后,需调用
setClickableViews注册绑定点击的View并刷新数据源refreshData:。 - 每次获取物料信息后需要刷新调用
refreshData:方法
@interface WindMillNativeAd : NSObject
/// Typed access to the ad title.
@property (nonatomic, copy, readonly) NSString *title;
/// Typed access to the body text, usually a longer description of the ad.
@property (nonatomic, copy, readonly) NSString *desc;
/// Typed access to the ad icon.
@property (nonatomic, copy, readonly) NSString *iconUrl;
/// 图片URL列表 【Optional,可能位nil】
/// 大图:列表中只有一个URL
/// 三图:列表中有3个URL
@property (nonatomic, strong) NSArray *imageUrlList;
/// 图片URL列表 【Optional,可能位nil】
/// 大图:列表中只有一个model
/// 三图:列表中有3个model
@property (nonatomic, strong, readonly, nullable) NSArray<AWMADImage *> *imageModelList;
/// 渠道ID
@property (nonatomic, assign) WindMillAdn networkId;
/// Typed access to the call to action phrase of the ad.
@property (nonatomic, copy, readonly) NSString *callToAction;
/// Typed access to the ad star rating.
@property (nonatomic, assign, readonly) double rating;
/// WindMillFeedADModeNativeExpress为模版渲染,此时其它属性都为nil
@property (nonatomic, assign, readonly) WindMillFeedADMode feedADMode;
/// 广告类型(Feed/Draw)
@property (nonatomic, assign, readonly) WindMillNativeAdSlotAdType adType;
/// 广告交互类型
@property (nonatomic, assign, readonly) WindMillInteractionType interactionType;
/// 是否为视频广告
@property (nonatomic, assign, readonly) BOOL isVideoAd;
/// 广告是否有效
@property (nonatomic, getter=isAdReady, readonly) BOOL ready;
/// 仅支持ks,设置广告是否支持点击和滑动效果
/// 强制要求:必须先设置 configuration,再调用 WindMillNativeAdView的 setClickableViews:
@property (nonatomic, strong, nullable) WindMillAdViewActionConfiguration *configuration;
@end4. 回调监听
4.1 WindMillNativeAdsManagerDelegate
| 回调 | 含义 | 使用场景 |
|---|---|---|
| nativeAdsManagerSuccessToLoad: | 广告加载成功 | 当次加载成功标识/自渲染可展示条件/加载数据统计 |
| nativeAdsManager: didFailWithError | 广告加载失败 | 重新加载条件/加载数据统计/问题排查 |
| nativeAdsManagerSuccessAutoToLoad: | 广告播放中加载成功回调 | 自动加载广告,流量填充时回调 |
| nativeAdsManager: didAutoFailWithError | 广告播放中加载出错 | 自动加载广告,流量填充失败时回调(无填充或者超时) |
| nativeAdsManager: didStartBidADSource | 竞价广告源开始竞价回调 | 广告源数据统计 |
| nativeAdsManager: didFinishBidADSource | 竞价广告源竞价成功回调 | 广告源数据统计 |
| nativeAdsManager: didFailBidADSource | 竞价广告源竞价失败回调,以及失败原因 | 广告源数据统计 |
| nativeAdsManager: didStartLoadingADSource | 广告源开始加载回调 | 广告源数据统计 |
| nativeAdsManager: didFinishLoadingADSource | 广告源广告填充回调 | 广告源数据统计 |
| nativeAdsManager: didFailToLoadADSource | 广告源加载失败回调,以及失败原因 | 广告源数据统计 |
@protocol WindMillNativeAdsManagerDelegate <NSObject>
@optional
- (void)nativeAdsManagerSuccessToLoad:(WindMillNativeAdsManager *)adsManager;
- (void)nativeAdsManager:(WindMillNativeAdsManager *)adsManager didFailWithError:(NSError *)error;
/// 广告播放中加载成功回调
/// - Parameter adsManager: WindMillNativeAdsManager 实例对象
- (void)nativeAdsManagerSuccessAutoToLoad:(WindMillNativeAdsManager *)adsManager;
/// 广告播放中加载出错
/// - Parameters:
/// - adsManager: WindMillNativeAdsManager 实例对象
/// - error: 具体错误信息
- (void)nativeAdsManager:(WindMillNativeAdsManager *)adsManager didAutoFailWithError:(NSError *)error;
/// 竞价广告源开始竞价回调
/// - Parameter nativeAdsManager: WindMillNativeAdsManager 实例对象
/// - nativeAdsManager: 开屏信息
/// - adInfo: 广告的相关信息
- (void)nativeAdsManager:(WindMillNativeAdsManager *)nativeAdsManager didStartBidADSource:(WindMillAdInfo *)adInfo;
/// 竞价广告源竞价成功回调
/// - Parameter nativeAdsManager: WindMillNativeAdsManager 实例对象
/// - nativeAdsManager: 开屏信息
/// - adInfo: 广告的相关信息
- (void)nativeAdsManager:(WindMillNativeAdsManager *)nativeAdsManager didFinishBidADSource:(WindMillAdInfo *)adInfo;
/// 竞价广告源竞价失败回调,以及失败原因
/// - Parameters:
/// - nativeAdsManager: WindMillNativeAdsManager 实例对象
/// - adInfo: 具体错误信息
- (void)nativeAdsManager:(WindMillNativeAdsManager *)nativeAdsManager didFailBidADSource:(WindMillAdInfo *)adInfo error:(NSError *)error;
/// 广告源开始加载回调
/// - Parameter nativeAdsManager: WindMillNativeAdsManager 实例对象
/// - nativeAdsManager: 开屏信息
/// - adInfo: 广告的相关信息
- (void)nativeAdsManager:(WindMillNativeAdsManager *)nativeAdsManager didStartLoadingADSource:(WindMillAdInfo *)adInfo;
/// 广告源广告填充回调
/// - Parameter nativeAdsManager: WindMillNativeAdsManager 实例对象
/// - nativeAdsManager: 开屏信息
/// - adInfo: 广告的相关信息
- (void)nativeAdsManager:(WindMillNativeAdsManager *)nativeAdsManager didFinishLoadingADSource:(WindMillAdInfo *)adInfo;
/// 广告源加载失败回调,以及失败原因
/// - Parameters:
/// - nativeAdsManager: WindMillNativeAdsManager 实例对象
/// - error: 具体错误信息
- (void)nativeAdsManager:(WindMillNativeAdsManager *)nativeAdsManager didFailToLoadADSource:(WindMillAdInfo *)adInfo error:(NSError *)error;
@end4.2 WindMillNativeAdViewDelegate
| 回调 | 含义 | 使用场景 |
|---|---|---|
| nativeExpressAdViewRenderSuccess: | 模版广告渲染成功 | 原生模板广告渲染成功, 此时的 nativeExpressAdView.size.height 根据 size.width 完成了动态更新 (只针对模版渲染) |
| nativeExpressAdViewRenderFail: error: | 模板广告渲染失败 | 原生模板广告渲染失败 (只针对模版渲染) |
| nativeAdViewWillExpose: | 广告曝光回调 | 展示数据统计,曝光后的时机可以获取AdInfo中的信息 |
| nativeAdViewDidClick: | 广告点击回调 | 点击数据统计 |
| nativeAdDetailViewClosed: | 广告详情页关闭回调 | 数据统计 |
| nativeAdDetailViewWillPresentScreen: | 广告详情页面即将展示回调 | 数据统计 |
| nativeAdView: playerStatusChanged: | 视频广告播放状态更改回调 | 数据统计 |
| nativeAdView: dislikeWithReason | 点击dislike回调 开发者需要在这个回调中移除视图,否则,会出现用户点击叉无效的情况 | 用户点击关闭广告/广告视图移除/数据统计 |
@protocol WindMillNativeAdViewDelegate <NSObject>
@optional
/**
* 原生模板广告渲染成功, 此时的 nativeExpressAdView.size.height 根据 size.width 完成了动态更新。
* (只针对模版渲染)
*/
- (void)nativeExpressAdViewRenderSuccess:(WindMillNativeAdView *)nativeExpressAdView;
/**
* 原生模板广告渲染失败
* (只针对模版渲染)
*/
- (void)nativeExpressAdViewRenderFail:(WindMillNativeAdView *)nativeExpressAdView error:(NSError *)error;
/**
广告曝光回调
@param nativeAdView WindMillNativeAdView 实例
*/
- (void)nativeAdViewWillExpose:(WindMillNativeAdView *)nativeAdView;
/**
广告点击回调
@param nativeAdView WindMillNativeAdView 实例
*/
- (void)nativeAdViewDidClick:(WindMillNativeAdView *)nativeAdView;
/**
广告详情页关闭回调
@param nativeAdView WindMillNativeAdView 实例
*/
- (void)nativeAdDetailViewClosed:(WindMillNativeAdView *)nativeAdView;
/**
广告详情页面即将展示回调
@param nativeAdView WindMillNativeAdView 实例
*/
- (void)nativeAdDetailViewWillPresentScreen:(WindMillNativeAdView *)nativeAdView;
/**
视频广告播放状态更改回调
@param nativeAdView WindMillNativeAdView 实例
@param status 视频广告播放状态
@param userInfo 视频广告信息
*/
- (void)nativeAdView:(WindMillNativeAdView *)nativeAdView playerStatusChanged:(WindMillMediaPlayerStatus)status userInfo:(NSDictionary *)userInfo;
/**
点击dislike回调
开发者需要在这个回调中移除视图,否则,会出现用户点击叉无效的情况
@param filterWords : 选择不喜欢的原因
*/
- (void)nativeAdView:(WindMillNativeAdView *)nativeAdView dislikeWithReason:(NSArray<WindDislikeWords *> *)filterWords;
@end5. 自渲染广告示例
广告加载和自渲染物料的获取:
// 广告加载
- (void)loadNativeAds {
WindMillAdRequest *request = [WindMillAdRequest request];
request.placementId = @"";
request.userId = @"user_id" // 开发者传入的userid建议不要超过65个字符, 超过限制后会导致数据计算异常
//nativeAdManager全局对象,每个广告位ID创建一个实例
if (self.nativeAdManager == nil) {
self.nativeAdManager = [[WindMillNativeAdsManager alloc] initWithRequest:request];
}
self.nativeAdManager.delegate = self;
self.nativeAdManager.adSize = CGSizeMake(320, 0);
[self.nativeAdManager setLoadCustomGroup:@{@"load_key":@"load_value"}];
[self.nativeAdManager loadAdDataWithCount:1];
}配套Demo主要使用refreshUIWithModel: 方法来进行具体的渲染布局:
- (void)refreshUIWithModel:(WindMillNativeAd *)model {
WindMillNativeAdView *adView = [WindMillNativeAdView new];
adView.delegate = self;
[adView refreshData:nativeAd];
adView.viewController = self;
[self.view addSubView:adView];
self.adView = adView;
[self renderAdWithLargeImg:model adView:adView];
}
+ (void)renderAdWithLargeImg:(WindMillNativeAd *)nativeAd adView:(NativeAdCustomView *)adView{
CGFloat width = CGRectGetWidth(UIScreen.mainScreen.bounds);
CGFloat contentWidth = (width - 2 * margin);
CGFloat imageHeight = 170;
[adView.mainImageView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(adView.mainImageView.superview).offset(padding.top);
make.left.equalTo(adView.mainImageView.superview).offset(padding.left);
make.right.equalTo(adView.mainImageView.superview).offset(-padding.right);
make.height.mas_equalTo(imageHeight);
}];
CGSize iconSize = CGSizeMake(60, 60);
NSURL *iconUrl = [NSURL URLWithString:nativeAd.iconUrl];
adView.iconImageView.layer.masksToBounds = YES;
adView.iconImageView.layer.cornerRadius = 10;
[adView.iconImageView sd_setImageWithURL:iconUrl];
[adView.iconImageView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(iconSize);
make.top.equalTo(adView.mainImageView.mas_bottom).offset(10);
make.left.equalTo(adView.iconImageView.superview).offset(padding.left);
}];
NSAttributedString *attributedText = [FeedStyleHelper titleAttributeText:nativeAd.title];
adView.titleLabel.attributedText = attributedText;
adView.titleLabel.lineBreakMode = NSLineBreakByTruncatingTail;
[adView.titleLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(adView.iconImageView.mas_top).offset(0);
make.left.equalTo(adView.iconImageView.mas_right).offset(padding.left);
make.right.equalTo(adView.CTAButton.mas_left).offset(-padding.right);
make.height.equalTo(@30);
}];
[adView.CTAButton setTitle:nativeAd.callToAction forState:UIControlStateNormal];
[adView.CTAButton mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(adView.iconImageView.mas_top).offset(0);
make.right.equalTo(adView.CTAButton.superview).offset(-padding.right);
make.width.equalTo(@80);
make.height.equalTo(@30);
}];
NSAttributedString *attributedDescText = [FeedStyleHelper titleAttributeText:nativeAd.desc];
CGSize descSize = [attributedDescText boundingRectWithSize:CGSizeMake(contentWidth, 0) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading context:0].size;
adView.descLabel.attributedText = attributedDescText;
adView.descLabel.numberOfLines = 0;
adView.descLabel.textColor = UIColor.blackColor;
[adView.descLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(adView.iconImageView.mas_bottom).offset(10);
make.left.equalTo(adView.descLabel.superview).offset(padding.left);
make.right.equalTo(adView.descLabel.superview).offset(-padding.right);
make.height.equalTo(@(descSize.height));
}];
[adView.dislikeButton mas_remakeConstraints:^(MASConstraintMaker *make) {
make.bottom.equalTo(adView).offset(-10);
make.right.equalTo(adView).offset(-10);
make.width.mas_equalTo(15);
make.height.mas_equalTo(15);
}];
UIView *logoView = (UIView *)adView.logoView;
[logoView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.bottom.equalTo(logoView.superview).offset(-10);
make.left.equalTo(logoView.superview).offset(10);
make.width.equalTo(@(70));
make.height.equalTo(@(20));
}];
[adView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.left.bottom.right.equalTo(adView.superview).offset(0);
make.width.mas_equalTo(UIScreen.mainScreen.bounds.size.width);
make.height.mas_equalTo(imageHeight + descSize.height + iconSize.height + 80);
}];
[adView setClickableViews:@[adView.CTAButton, adView.mainImageView, adView.iconImageView]];
}注意:Demo针对大图渲染示例,需要注意的是视频类型的渲染需要使用mediaView
6 广告预加载注意事项
- 需要在变现平台开启预加载功能
- 当广告开始播放时,ToBid SDK会自动执行下一条广告加载逻辑,当下一条广告加载成功后,ToBid SDK会再次回调开发者
nativeAdsManagerSuccessAutoToLoad:广告播放中加载成功的回调。 - WindMillNativeAdsManager每个广告位ID创建一个实例对象,多个实例对象之间的预加载逻辑互不干扰。
WindMillAdRequest *request = [WindMillAdRequest request];
request.placementId = @"";
request.userId = @"user_id"
//nativeAdManager全局对象,每个广告位ID创建一个实例
if (self.nativeAdManager == nil) {
self.nativeAdManager = [[WindMillNativeAdsManager alloc] initWithRequest:request];
}
self.nativeAdManager.delegate = self;
self.nativeAdManager.adSize = CGSizeMake(320, 0);
[self.nativeAdManager loadAdDataWithCount:1];7. 模板示例
模板示例的加载和自渲染广告加载相同,不同的是在渲染上更简单,不需要关心标题、描述、icon、大图、视频等元素
- (void)refreshUIWithModel:(WindMillNativeAd *)model {
WindMillNativeAdView *adView = [WindMillNativeAdView new];
adView.delegate = self;//在refreshData之前设置delegate
[adView refreshData:nativeAd];
adView.viewController = self;//在refreshData之后设置viewController
[self.view addSubView:adView];
self.adView = adView;
[adView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.left.bottom.equalTo(adView.superview).offset(0);
make.width.mas_equalTo(adView.frame.size.width);
make.height.mas_equalTo(adView.frame.size.height);
}];
}注意:模版渲染的广告在nativeExpressAdViewRenderSuccess回调后,adView.size.height会根据width自动调整到适合的值
8. 特别说明
加载广告时,模版渲染和自渲染统一使用WindMillNativeAdsManager加载广告,在广告加载成功回调中,可以通过getAllNativeAds获取所有可用广告模型,通过模型中的feedADMode区分是模版渲染还是非模版渲染。
模版渲染布局相对简单,只需要最整个WindMillNativeAdView做布局即可
模版渲染传入adSize的高度为0时,需要在渲染成功的回调调整view的高度
自渲染需要最标题、描述、icon、视频/大图、cta按钮做自定义布局