- 包含视频内容的原生广告。 广告的
mediaView包含视频播放器,其他素材(标题、正文、图标、CTA)覆盖在视频之上。 - 视频播放设置。 加载器上的三个属性分别用于禁用全屏、开启有声播放和隐藏媒体控件。
- 全屏分页容器。 启用分页的
UICollectionView配合垂直流布局,每个单元格即为一个 Reel。
Reels API 概览
| 功能 | CloudX API | 说明 |
|---|---|---|
| 检测视频素材 | ad.nativeAd.isVideoContent | 当加载的素材为视频时返回 YES |
| 获取视频时长 | ad.nativeAd.videoDuration | 视频时长(秒),未知时为 0 |
| 用户关闭广告 | didCloseNativeAd: 代理回调 | 当用户通过 AdChoices 举报或隐藏广告时触发 |
| 禁用全屏 | loader.disableVideoFullScreen = YES | 阻止视频在点击时进入全屏模式 |
| 有声播放 | loader.startVideoUnmuted = YES | 开始播放时开启声音 |
| 隐藏媒体控件 | loader.hideVideoMediaControls = YES | 隐藏播放/暂停和静音/取消静音控件 |
原生广告适配器支持情况和依赖要求记录在各适配器页面中。
支持的网络
原生广告(包括 Native Banner 和 Native MREC 变体)由以下竞价网络提供:| 网络 | Native | Native Banner | Native MREC |
|---|---|---|---|
| Meta Audience Network | ✓ | ✓ | ✓ |
| Digital Turbine | ✓ | ✓ | ✓ |
| InMobi | ✓ | ✓ | ✓ |
| Mintegral | ✓ | ✓ | ✓ |
| Moloco | ✓ | ✓ | ✓ |
| Pangle | ✓ | ✓ | ✓ |
| Verve | ✓ | ✓ | ✓ |
| Vungle | ✓ | ✓ | ✓ |
创建加载器并配置视频
@interface YourViewController () <CLXNativeAdDelegate, CLXAdRevenueDelegate>
@property (nonatomic, strong) CLXNativeAdLoader *nativeAdLoader;
@end
@implementation YourViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.nativeAdLoader = [[CloudXCore shared] createNativeAdLoaderWithAdUnitIdentifier:@"your-native-ad-unit-id"];
self.nativeAdLoader.nativeAdDelegate = self;
self.nativeAdLoader.revenueDelegate = self;
self.nativeAdLoader.disableVideoFullScreen = YES;
self.nativeAdLoader.startVideoUnmuted = YES;
self.nativeAdLoader.hideVideoMediaControls = YES;
}
- (void)dealloc {
[self.nativeAdLoader destroy];
}
@end
class YourViewController: UIViewController, CLXNativeAdDelegate, CLXAdRevenueDelegate {
private var nativeAdLoader: CLXNativeAdLoader?
override func viewDidLoad() {
super.viewDidLoad()
nativeAdLoader = CloudXCore.shared.createNativeAdLoader(adUnitIdentifier: "your-native-ad-unit-id")
nativeAdLoader?.nativeAdDelegate = self
nativeAdLoader?.revenueDelegate = self
nativeAdLoader?.disableVideoFullScreen = true
nativeAdLoader?.startVideoUnmuted = true
nativeAdLoader?.hideVideoMediaControls = true
}
deinit {
nativeAdLoader?.destroy()
}
}
| 属性 | 默认值 | Reels 值 | 说明 |
|---|---|---|---|
disableVideoFullScreen | NO | YES | 阻止视频在点击时进入全屏模式 |
startVideoUnmuted | NO | YES | 开始播放时开启声音 |
hideVideoMediaControls | NO | YES | 隐藏播放/暂停和静音/取消静音控件 |
loadAd 之前设置以上属性。对静态图片素材无效。对于 Reels 风格的信息流,将三个属性全部设置为 YES。
构建原生广告布局
创建CLXNativeAdView,使用视图绑定器将自定义子视图映射到素材角色:
- (CLXNativeAdView *)createNativeAdView {
CLXNativeAdViewBinder *binder = [[CLXNativeAdViewBinder alloc] initWithBuilderBlock:^(CLXNativeAdViewBinderBuilder *builder) {
builder.titleLabelTag = CLXNativeAdViewTagTitleLabel;
builder.bodyLabelTag = CLXNativeAdViewTagBodyLabel;
builder.iconImageViewTag = CLXNativeAdViewTagIconImageView;
builder.callToActionButtonTag = CLXNativeAdViewTagCallToActionButton;
builder.mediaContentViewTag = CLXNativeAdViewTagMediaViewContainer;
builder.optionsContentViewTag = CLXNativeAdViewTagOptionsContentView;
builder.advertiserLabelTag = CLXNativeAdViewTagAdvertiserLabel;
}];
CLXNativeAdView *adView = [[CLXNativeAdView alloc] init];
[adView bindViewsWithViewBinder:binder];
return adView;
}
func createNativeAdView() -> CLXNativeAdView {
let binder = CLXNativeAdViewBinder { builder in
builder.titleLabelTag = CLXNativeAdViewTagTitleLabel
builder.bodyLabelTag = CLXNativeAdViewTagBodyLabel
builder.iconImageViewTag = CLXNativeAdViewTagIconImageView
builder.callToActionButtonTag = CLXNativeAdViewTagCallToActionButton
builder.mediaContentViewTag = CLXNativeAdViewTagMediaViewContainer
builder.optionsContentViewTag = CLXNativeAdViewTagOptionsContentView
builder.advertiserLabelTag = CLXNativeAdViewTagAdvertiserLabel
}
let adView = CLXNativeAdView()
adView.bindViews(with: binder)
return adView
}
CLXNativeAdView 上设置 outlet:
CLXNativeAdView *adView = [[CLXNativeAdView alloc] init];
adView.titleLabel = myTitleLabel;
adView.bodyLabel = myBodyLabel;
adView.iconImageView = myIconImageView;
adView.callToActionButton = myCTAButton;
adView.mediaContentView = myMediaContainer;
adView.optionsContentView = myOptionsContainer;
adView.advertiserLabel = myAdvertiserLabel;
let adView = CLXNativeAdView()
adView.titleLabel = myTitleLabel
adView.bodyLabel = myBodyLabel
adView.iconImageView = myIconImageView
adView.callToActionButton = myCTAButton
adView.mediaContentView = myMediaContainer
adView.optionsContentView = myOptionsContainer
adView.advertiserLabel = myAdvertiserLabel
加载广告
流程 A — 加载到预构建的视图中:CLXNativeAdView *adView = [self createNativeAdView];
[self.nativeAdLoader loadAdIntoAdView:adView];
let adView = createNativeAdView()
nativeAdLoader?.loadAd(into: adView)
[self.nativeAdLoader loadAd];
- (void)didLoadNativeAd:(nullable CLXNativeAdView *)nativeAdView forAd:(CLXAd *)ad {
CLXNativeAdView *adView = /* 创建广告视图 */;
[self.nativeAdLoader renderNativeAdView:adView withAd:ad];
[self.view addSubview:adView];
}
nativeAdLoader?.loadAd()
func didLoadNativeAd(_ nativeAdView: CLXNativeAdView?, for ad: CLXAd) {
let adView = /* 创建广告视图 */
nativeAdLoader?.renderNativeAdView(adView, with: ad)
view.addSubview(adView)
}
处理回调
#pragma mark - CLXNativeAdDelegate(必选)
- (void)didLoadNativeAd:(nullable CLXNativeAdView *)nativeAdView forAd:(CLXAd *)ad {
NSLog(@"Native ad loaded from %@", ad.networkName);
if (ad.nativeAd.isVideoContent) {
NSLog(@"Video duration: %.1fs", ad.nativeAd.videoDuration);
}
if (nativeAdView) {
[self.view addSubview:nativeAdView];
}
}
- (void)didFailToLoadNativeAdForAdUnitIdentifier:(NSString *)adUnitId error:(CLXError *)error {
NSLog(@"Native ad failed to load: %@", error.localizedDescription);
}
- (void)didClickNativeAd:(CLXAd *)ad {
NSLog(@"Native ad clicked");
}
#pragma mark - CLXNativeAdDelegate(可选)
- (void)didExpireNativeAd:(CLXAd *)ad {
NSLog(@"Native ad expired — destroy and reload");
[self.nativeAdLoader destroyAd:ad];
[self.nativeAdLoader loadAd];
}
- (void)didCloseNativeAd:(CLXAd *)ad {
NSLog(@"User dismissed the ad via AdChoices");
[self.nativeAdLoader destroyAd:ad];
}
#pragma mark - CLXAdRevenueDelegate
- (void)didPayRevenueForAd:(CLXAd *)ad {
NSLog(@"Native ad revenue: %@ from %@", ad.revenue, ad.networkName);
}
// MARK: - CLXNativeAdDelegate(必选)
func didLoadNativeAd(_ nativeAdView: CLXNativeAdView?, for ad: CLXAd) {
print("Native ad loaded from \(ad.networkName ?? "unknown")")
if let nativeAd = ad.nativeAd, nativeAd.isVideoContent {
print("Video duration: \(nativeAd.videoDuration)s")
}
if let nativeAdView = nativeAdView {
view.addSubview(nativeAdView)
}
}
func didFailToLoadNativeAd(forAdUnitIdentifier adUnitId: String, error: CLXError) {
print("Native ad failed to load: \(error.localizedDescription)")
}
func didClickNativeAd(_ ad: CLXAd) {
print("Native ad clicked")
}
// MARK: - CLXNativeAdDelegate(可选)
func didExpireNativeAd(_ ad: CLXAd) {
print("Native ad expired — destroy and reload")
nativeAdLoader?.destroyAd(ad)
nativeAdLoader?.loadAd()
}
func didCloseNativeAd(_ ad: CLXAd) {
print("User dismissed the ad via AdChoices")
nativeAdLoader?.destroyAd(ad)
}
// MARK: - CLXAdRevenueDelegate
func didPayRevenue(for ad: CLXAd) {
print("Native ad revenue: \(ad.revenue ?? 0) from \(ad.networkName ?? "unknown")")
}
清理资源
使用完毕后务必销毁广告:// 销毁特定已加载的广告
[self.nativeAdLoader destroyAd:ad];
// 销毁加载器及所有关联资源
[self.nativeAdLoader destroy];
// 销毁特定已加载的广告
nativeAdLoader?.destroyAd(ad)
// 销毁加载器及所有关联资源
nativeAdLoader?.destroy()
原生广告素材(CLXNativeAd)
CLXNativeAd 对象可通过代理回调中的 ad.nativeAd 获取:
| 属性 | 类型 | 说明 |
|---|---|---|
title | NSString? | 标题文本 |
body | NSString? | 正文/描述文本 |
callToAction | NSString? | CTA 按钮文本(如 “Install Now”) |
advertiser | NSString? | 广告主名称 |
icon | CLXNativeAdImage? | 应用图标图片 |
mainImage | CLXNativeAdImage? | 主图片(静态素材) |
mediaView | UIView? | 视频/媒体播放器视图(由适配器提供) |
optionsView | UIView? | AdChoices 或选项视图(由适配器提供) |
mediaContentAspectRatio | CGFloat | 媒体内容宽高比 |
starRating | NSNumber? | 应用商店评分(0–5) |
isVideoContent | BOOL | 素材是否为视频 |
videoDuration | NSTimeInterval | 视频时长(秒),未知时为 0 |
expired | BOOL | 广告是否已过期 |
Reels 信息流使用提示
- 使用
UICollectionView,设置pagingEnabled = YES并配合垂直方向的UICollectionViewFlowLayout,设置minimumLineSpacing = 0,每个单元格全屏显示。 - 在回收单元格时调用
CLXNativeAdView的prepareForReuse。 - 每个广告位创建一个
CLXNativeAdLoader,按顺序加载广告。 - 不再需要广告时通过
destroyAd:销毁。