主题
横幅 —— NovvyBannerAd
固定尺寸的常驻内联横幅,挂在宿主给的容器里,到点自动换一支新创意(素材类型为图片)。
与其余七个入口的根本差别:它不是「一次契机 → 一支广告 → 结束」,而是「一块地方,一直有广告」。所以宿主只调一次 load(),之后的请求、换屏、失败重试全部由 SDK 自己驱动,直到 destroy()。
⚠️ 不要在
bannerAdDidLoad或bannerAd(_:didFailToLoadWithError:)里再调load()—— 轮换与失败退避都是自动的,重复调用会被忽略并留下一条 warning。
对接流程
init(adUnitId:size:adContainer:) // 立刻在容器内建好槽位(不发请求)
↓
load() // 只调一次
↓
bannerAdDidLoad → bannerAdDidShow // 首支创意上屏
↓ (每隔 refreshIntervalSeconds 秒重复)
bannerAdDidLoad → bannerAdDidShow // 换上下一支,无白帧
↓
destroy() → bannerAdDidHide // 停轮换、撤创意、把槽位从容器移除1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
创建
swift
final class MyPageController: UIViewController, NovvyBannerAdDelegate {
private var banner: NovvyBannerAd?
override func viewDidLoad() {
super.viewDidLoad()
// bannerContainer 是你自己布好的一块 ≥320×50pt 的区域
let ad = NovvyBannerAd(
adUnitId: "your_ad_unit_id",
size: .standard,
adContainer: bannerContainer,
refreshIntervalSeconds: 60 // 可省;夹在 [30, 120],传 <=0 关闭轮换
)
ad.delegate = self
ad.load() // 只调一次
banner = ad
}
deinit {
banner?.destroy()
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
构造参数
| 参数 | 说明 |
|---|---|
adUnitId | banner 类型的广告位标识 |
size | NovvyBannerSize 档位,默认 .standard,见下表 |
adContainer | 宿主容器。SDK 不接管容器:它在容器内部另建一层自己的槽位视图并居中钉死成声明尺寸,容器里原有的宿主 UI 一律不受影响 |
refreshIntervalSeconds | 轮换间隔秒数,默认 60,会被夹进 [30, 120](超出范围留 warning)。传 <= 0 表示关闭轮换 —— 只展示一支,且首次填充最多尝试 3 次 |
尺寸档位(NovvyBannerSize,IAB 标准五档;单位 dp,iOS 上即 pt)
请给容器预留不小于所声明档位的宽高。给不够时 SDK 会留下一条写明所需尺寸的 warning,创意被裁切且不会产生曝光(可见面积过不了判据)。
| 档位 | 尺寸 | 说明 |
|---|---|---|
.standard | 320 × 50 | 默认档,IAB Mobile Leaderboard |
.large | 320 × 100 | IAB Large Mobile Banner |
.mediumRectangle | 300 × 250 | IAB Medium Rectangle。位图较大,内存紧张时优先选前两档 |
.fullBanner | 468 × 60 | IAB Full Banner,竖屏手机宽度放不下 |
.leaderboard | 728 × 90 | IAB Leaderboard,平板 / 横屏专用 |
槽位一经构造就恒定占位,填不上广告也不会折叠 —— 否则下方内容会在滑动中上跳。素材只接 IMAGE,按 cover 铺满(比例不合时裁掉溢出部分,不留边 —— 留边会在宿主页面上开两个洞)。
轮换与失败重试
- 预取式轮换:「满间隔且素材就绪」才替换,替换是瞬时的,不会出现空窗。
- 预取失败不下屏:屏上那支不动,SDK 在其下退避重试(间隔逐次翻倍、5 分钟封顶)。
bannerAd(_:didFailToLoadWithError:)会随之反复触发,这是正常的,宿主不需要、也不应该在这里重新调load()。 - 后台不轮换:广告滑出屏幕、或 App 切到后台时轮换自动暂停,回来继续 —— 宿主不需要(也没有接口)手动通知。
- 只需要一个实例:轮换是 SDK 内部行为,不要为了「换一支」而重建
NovvyBannerAd。destroy()幂等;之后再调load()无效,需要新广告位请新建实例。所有方法必须在主线程调用。
事件回调
NovvyBannerAdDelegate 五个方法全部有默认空实现,按需重写;回调统一在主线程触发。
| 回调 | 触发时机 |
|---|---|
bannerAdDidLoad(_:) | 一支创意预取就绪。此时还没上屏 —— 轮换是「先取好再换」,素材到手与上屏之间通常隔着剩下的那段间隔(首次填充是唯一两者几乎同时的情况) |
bannerAd(_:didFailToLoadWithError:) | 一次填充失败。屏上那支不动,SDK 自行退避重试,因此本回调会随重试反复触发;error 取值见错误类型 |
bannerAdDidShow(_:) | 一支创意上屏、曝光上报完成。轮换开着时每轮一次 |
bannerAdDidClick(_:) | 用户点击、落地页由 SDK 打开。横幅是全 SDK 唯一有点击回调的类型(其余类型的点击紧跟着浏览器跳转,宿主自己观察得到;常驻槽位给不了这个信号) |
bannerAdDidHide(_:) | 槽位空了 —— 当前只有 destroy() 一条路径(轮换换屏是「换」不是「空」,不触发本回调) |
完整示例
swift
final class MyPageController: UIViewController, NovvyBannerAdDelegate {
private var banner: NovvyBannerAd?
override func viewDidLoad() {
super.viewDidLoad()
let ad = NovvyBannerAd(
adUnitId: "your_ad_unit_id",
size: .standard,
adContainer: bannerContainer,
refreshIntervalSeconds: 60
)
ad.delegate = self
ad.load()
banner = ad
}
deinit {
banner?.destroy()
}
// MARK: - NovvyBannerAdDelegate(按需重写)
func bannerAdDidLoad(_ ad: NovvyBannerAd) { /* 素材就绪,尚未上屏 */ }
func bannerAd(_ ad: NovvyBannerAd, didFailToLoadWithError error: NovvyError) { /* 屏上那支不动,SDK 自行重试 */ }
func bannerAdDidShow(_ ad: NovvyBannerAd) { /* 每轮上屏一次 */ }
func bannerAdDidClick(_ ad: NovvyBannerAd) { }
func bannerAdDidHide(_ ad: NovvyBannerAd) { /* 槽位空了 */ }
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27