主题
Banner 广告
文档版本 v1.0.0
本页对应 iOS SDK 1.0.0,与离线包 docs/ 目录里的同一份文档内容一致。其他版本见文档中心。
与另两种广告位最大的区别是它常驻页面并定时刷新,因此生命周期管理是必做项, 漏掉 destroy 广告会一直在刷新。
完整示例
objc
// BannerViewController.h
#import <UIKit/UIKit.h>
@interface BannerViewController : UIViewController
@endobjc
// BannerViewController.m
#import "BannerViewController.h"
#import <AdMixCore/AdMixCore.h>
@interface BannerViewController () <AdMixBannerAdDelegate>
@end
@implementation BannerViewController {
AdMixBannerAd *_bannerAd;
UIView *_bannerContainer;
NSLayoutConstraint *_bannerWidth;
NSLayoutConstraint *_bannerHeight;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = UIColor.whiteColor;
// 容器专门给广告用。大小在每次填充后按 adSize 设,没有广告时高度为 0、不占位
_bannerContainer = [UIView new];
_bannerContainer.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:_bannerContainer];
_bannerWidth = [_bannerContainer.widthAnchor constraintEqualToConstant:0];
_bannerHeight = [_bannerContainer.heightAnchor constraintEqualToConstant:0];
[NSLayoutConstraint activateConstraints:@[
[_bannerContainer.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
[_bannerContainer.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor],
_bannerWidth, _bannerHeight,
]];
_bannerAd = [[AdMixBannerAd alloc] initWithUnitId:@"banner_main"];
_bannerAd.delegate = self;
[_bannerAd loadInContainer:_bannerContainer viewController:self];
}
#pragma mark - AdMixBannerAdDelegate
// 每次刷新都会回调,中标方可能变、各家规格可能不同,所以每次都按 adSize 重设容器大小
- (void)bannerAdDidLoad:(AdMixBannerAd *)ad adnType:(NSString *)adnType {
_bannerWidth.constant = ad.adSize.width;
_bannerHeight.constant = ad.adSize.height;
}
- (void)bannerAd:(AdMixBannerAd *)ad didFailWithError:(NSError *)error {
// 没有广告时容器保持空白即可;定时刷新照常进行,下一轮有填充会自动补上
NSLog(@"Banner 加载失败 %@ %ld %@", error.domain, (long)error.code, error.localizedDescription);
}
// 用户关闭后刷新自动停止,收起容器
- (void)bannerAdDidClose:(AdMixBannerAd *)ad {
_bannerHeight.constant = 0;
}
- (void)dealloc {
[_bannerAd destroy];
}
@end要点:
- 广告视图由 SDK 加进容器,业务侧不需要自己
addSubview viewController是承载页面,用户点击广告时广告平台用它弹出落地页bannerAdDidLoad:adnType:每次刷新成功都会回调,adnType可能与上次不同,容器大小在这里按adSize重设bannerAdDidClose:表示用户点了广告自带的关闭按钮,此后刷新自动停止
容器只给广告用
新旧广告交替与 destroy 时,SDK 会清空容器内的全部子视图。放在容器里的其他视图(标题、占位图、分隔线)会被一并移除。
页面销毁时必须调 destroy
Banner 的刷新定时器一直在转。页面关闭后不 destroy 的后果是内存持续上涨 + 曝光数虚高 (广告还在刷新并上报曝光,而页面已经没了)。示例里放在 dealloc; 页面被导航栈长期持有、不会及时释放时,改为在离开页面的时机调用。
刷新由聚合层驱动
刷新间隔由后台的 refresh_interval_sec 决定,每次刷新重走一整轮完整调度 (竞价、比价、竞胜 / 竞败回传一条不少),业务侧不需要任何干预。
两个适配器都显式关掉了广告平台自带的轮播。不这样做的后果是广告位会永远锁死在最初中标的那一家, 聚合失去意义。
广告平台后台的 Banner 自动刷新也要关掉
两边都开会重复计费曝光,也会打乱聚合比价。这一项由平台运营在广告平台后台确认。
新旧广告交替时,SDK 先挂新广告再移除旧的 —— 反过来会有空白闪烁。
尺寸规格
Banner 的尺寸分两层,出问题时要先分清是哪一层:
| 决定于 | 影响 | 谁维护 | |
|---|---|---|---|
| 请求尺寸 | 服务端下发的 width_dp / height_dp(iOS 上按 point 解释) | 向广告平台请求什么规格的素材,也是广告视图的大小 | 平台运营在后台配置 |
| 容器尺寸 | 宿主布局里那个容器视图的宽高 | 广告在页面上的位置与可见区域 | 接入方 |
请求尺寸必须与广告平台后台建该广告位时选定的规格一致,否则穿山甲报 40006 广告位ID不合法,或者返回一条比例怪异的素材。
这个错误码会把排查引向错误的方向
40006 的字面意思是「广告位 ID 不合法」,而真实原因是尺寸规格对不上,与 ID 本身无关。 调整宿主布局里的容器宽高解决不了这个问题 —— 请求尺寸来自服务端配置,不读容器。 处置方式是联系平台运营,核对后台配置的尺寸与广告平台侧登记的规格。
iOS 上两家广告平台的广告视图都按请求尺寸生成,以容器左上角为原点放入容器,不随容器缩放。 因此容器宽高应当等于当前广告视图的大小:容器小于它时,广告不会被裁剪,而是伸出容器之外, 盖住相邻的内容,容器贴着屏幕边缘时还会有一部分跑到屏幕外(真机实测:320×50 的容器贴底放置, 300×150 的素材下半截在屏幕外);容器大于它时多出的部分留白。
当前广告视图的大小由 adSize 给出(单位 point,没有在展示的广告时为 CGSizeZero)。 每次 bannerAdDidLoad:adnType: 都按它重设一次容器大小,如上方示例 —— 不要把容器宽高写死。
同一广告位下的各家规格应当一致
一个 Banner 广告位会向多家广告平台请求,中标方要到竞价结束才知道。 若各家在自己后台登记的尺寸规格不同(例如一家 320×50、另一家 320×150), 宿主无法事先按正确大小准备容器,只能在填充后按 adSize 调整,页面布局会随中标方跳动。 因此同一广告位下各广告平台应登记同一尺寸规格。
向平台运营索要 Banner 广告位时,必须同时确认每个广告位的尺寸规格 —— 只拿到广告位标识、没拿到尺寸,要到真机上才会发现对不上。
缓存
Banner 只有首次加载会取缓存,定时刷新一律实时请求、不自动补位。 配置未下发 cache_size 时 Banner 默认为 0(不缓存)—— 缓存只对首屏有价值。
Swift 写法
swift
import AdMixCore
final class BannerViewController: UIViewController, AdMixBannerAdDelegate {
private var bannerAd: AdMixBannerAd?
private let bannerContainer = UIView() // 专门给广告用
private lazy var bannerWidth = bannerContainer.widthAnchor.constraint(equalToConstant: 0)
private lazy var bannerHeight = bannerContainer.heightAnchor.constraint(equalToConstant: 0)
override func viewDidLoad() {
super.viewDidLoad()
bannerContainer.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(bannerContainer)
NSLayoutConstraint.activate([
bannerContainer.centerXAnchor.constraint(equalTo: view.centerXAnchor),
bannerContainer.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
bannerWidth, bannerHeight, // 填充后按 adSize 设
])
let ad = AdMixBannerAd(unitId: "banner_main")
ad.delegate = self
bannerAd = ad
ad.load(inContainer: bannerContainer, viewController: self)
}
// 每次定时刷新成功都会回调,中标方与规格都可能变,每次按 adSize 重设容器大小
func bannerAdDidLoad(_ ad: AdMixBannerAd, adnType: String) {
bannerWidth.constant = ad.adSize.width
bannerHeight.constant = ad.adSize.height
}
func bannerAd(_ ad: AdMixBannerAd, didFailWithError error: Error) {}
func bannerAdDidClose(_ ad: AdMixBannerAd) { bannerHeight.constant = 0 } // 用户关闭后刷新自动停止
deinit {
// 不 destroy 的话页面关闭后定时器还在转:内存持续上涨 + 曝光数虚高
bannerAd?.destroy()
}
}验证刷新是否正常
停在 Banner 页看 2~3 个刷新周期,确认无空白、无闪烁、内存不持续上涨。
刷新后「看着没变」通常不是故障
测试广告位的素材池小且风格雷同,肉眼看着像没变,实际已经换了一条。 用刷新计数(bannerAdDidLoad:adnType: 回调次数)确认,不要靠肉眼。