Skip to content

原生广告(模版\自渲染\draw)

参考Demo 原生广告分为自渲染广告和模板广告,但是模板广告只有当三方SDK支持时才会返回模板广告。SDK 提供信息流广告的数据绑定、点击事件的上报

  • 自渲染广告:聚合SDK返回物料,由开发者在返回的WindMillNativeAdView类型的view上进行子视图的自行渲染和展示。
  • 模板广告:聚合SDK直接返回渲染好的广告WindMillNativeAdView,开发者直接展示即可。
  • isAdReady方法430版本开始 由WindMillNativeAdsManager 转移到 WindMillNativeAd 对象下,针对每条播放的广告进行判断。

1. 原生广告管理类WindMillNativeAdsManager

在SDK里只需要使用 WindMillNativeAdsManager 就可以获取信息流广告。WindMillNativeAdsManager支持多广告加载,可以一次加载返回多个广告。

注意:adSize高度为0时,模版渲染render success后自动更新到合适的高度

objective-c
@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;

@end

2. 原生广告类WindMillNativeAdView

WindMillNativeAdView:为请求原生广告返回的广告模型后,创建WindMillNativeAdView后,使用refreshData:渲染

  • 自渲染广告(feedADMode != WindMillFeedADModeNativeExpress) 开发者根据WindMillNativeAdView提供的组件和WindMillNativeAd提供的物料信息对WindMillNativeAdView进行布局。
  • 模板广告(feedADMode = WindMillFeedADModeNativeExpress) 开发者可直接将ABUNativeAdView添加到父视图上进行展示。

注意:目前布局方式支持:frame布局方式 和 自动布局方式

objective-c
@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;

@end

3. 自渲染广告物料信息

  1. 在物料加载成功方法里获取相关广告信息赋值后,需调用 setClickableViews注册绑定点击的View并刷新数据源refreshData:。
  2. 每次获取物料信息后需要刷新调用refreshData:方法
objective-c
@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;

@end

4. 回调监听

4.1 WindMillNativeAdsManagerDelegate

回调含义使用场景
nativeAdsManagerSuccessToLoad:广告加载成功当次加载成功标识/自渲染可展示条件/加载数据统计
nativeAdsManager: didFailWithError广告加载失败重新加载条件/加载数据统计/问题排查
nativeAdsManagerSuccessAutoToLoad:广告播放中加载成功回调自动加载广告,流量填充时回调
nativeAdsManager: didAutoFailWithError广告播放中加载出错自动加载广告,流量填充失败时回调(无填充或者超时)
nativeAdsManager: didStartBidADSource竞价广告源开始竞价回调广告源数据统计
nativeAdsManager: didFinishBidADSource竞价广告源竞价成功回调广告源数据统计
nativeAdsManager: didFailBidADSource竞价广告源竞价失败回调,以及失败原因广告源数据统计
nativeAdsManager: didStartLoadingADSource广告源开始加载回调广告源数据统计
nativeAdsManager: didFinishLoadingADSource广告源广告填充回调广告源数据统计
nativeAdsManager: didFailToLoadADSource广告源加载失败回调,以及失败原因广告源数据统计
objective-c
@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;


@end

4.2 WindMillNativeAdViewDelegate

回调含义使用场景
nativeExpressAdViewRenderSuccess:模版广告渲染成功原生模板广告渲染成功, 此时的 nativeExpressAdView.size.height 根据 size.width 完成了动态更新
(只针对模版渲染)
nativeExpressAdViewRenderFail: error:模板广告渲染失败原生模板广告渲染失败
(只针对模版渲染)
nativeAdViewWillExpose:广告曝光回调展示数据统计,曝光后的时机可以获取AdInfo中的信息
nativeAdViewDidClick:广告点击回调点击数据统计
nativeAdDetailViewClosed:广告详情页关闭回调数据统计
nativeAdDetailViewWillPresentScreen:广告详情页面即将展示回调数据统计
nativeAdView: playerStatusChanged:视频广告播放状态更改回调数据统计
nativeAdView: dislikeWithReason点击dislike回调
开发者需要在这个回调中移除视图,否则,会出现用户点击叉无效的情况
用户点击关闭广告/广告视图移除/数据统计
objective-c
@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;

@end

5. 自渲染广告示例

广告加载和自渲染物料的获取:

objective-c
// 广告加载
- (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: 方法来进行具体的渲染布局:

objective-c
- (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创建一个实例对象,多个实例对象之间的预加载逻辑互不干扰。
objective-c
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、大图、视频等元素

objective-c
- (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按钮做自定义布局