The VIS.X® SDK dynamically adjusts the size of an inline banner position to realize high-impact effects, such as the YOC Understitial Ad®.
To unlock the full potential of YOC’s high-impact advertising formats, it’s advised to integrate the banner placement inside scrollable content, e.g. inside an UIScrollView
, UITableView
or UICollectionView
and allow resizing of the adContainer upon request.
An adContainer is needed to perform an ad call. It requires a VIS.X® Ad Unit ID (provided by YOC).
- Importing VisxSDK to your UIViewController
- Initializing VisxAdView for an ad request
- Rendering your ad into your AdContainer
- Specifics for UITableView, UICollectionView
- Providing the anchorFrame to VisxAdView
Importing VisxSDK to your UIViewController
First, import VisxSDK
into your UIViewController
.
import VisxSDK
Initializing VisxAdView for an ad request
A VisxAdView
represents an instance of an ad you want to deliver and to be rendered into an AdContainer (a UIView
that holds the creative) inside your UIViewController
.
The VIS.X® SDK will offer two convenient ways to initialize and load the VisxAdView
for you to choose from. You have the choice to use either delegates or closures as callbacks.
// Using the initializer with callbacks
adView = VisxAdView(adUnit: "123456",
adSize: CGSize(width: 300, height: 250),
fixedSize: Bool? = false)
adView?.callbackHandler?
.onInitialize { [weak self] visxAdView, effect in
self?.containerHeightConstraint.constant = visxAdView.frame.size.height
self?.adContainer.addSubview(visxAdView)
}
adView?.load()
// Using the initializer with delegates
defaultAdView = VisxAdView(adUnit: "123456",
adViewDelegate: self,
adSize: CGSize(width: 300, height: 250),
interstitial: Bool? = false)
defaultAdView?.load()
Parameter | Description |
---|---|
adUnit |
VIS.X® Ad Unit ID (provided by YOC) |
adViewDelegate |
VisxAdViewDelegate , usually self |
adSize |
adSize is the default CGSize of your adContainer |
interstitial |
Always set to false for banner. |
Rendering your ad into your AdContainer
The requested creative needs to be rendered into an adContainer (UIView
) and to allow proper resizing, this adContainer needs to have specified a heightConstraint of type NSLayoutConstraint
.
class MyViewController: UIViewController, UIScrollViewDelegate {
@IBOutlet weak var adContainer: UIView!
@IBOutlet weak var containerHeightConstraint: NSLayoutConstraint!
/* ... */
}
Next, you need to either conform to the VisxAdViewDelegate
or Closures
as callbacks.
Using Delegates
extension MyViewController: VisxAdViewDelegate {
/// required methods
/// ViewController for presenting VisxAdView
func viewControllerForPresentingVisxAdView() -> UIViewController {
self
}
/// Delegate method is called when the ad content is received for the first time and the VisxAdView has finished rendering the content.
func visxAdViewDidInitialize(visxAdView: VisxAdView, effect: VisxPlacementEffect) {
containerHeightConstraint.constant = visxAdView.frame.size.height
adContainer.addSubview(visxAdView)
}
/// Delegate method called when universal creative changes dimensions
func visxAdViewSizeChange(visxAdView: VisxAdView, width: CGFloat, height: CGFloat) {
containerHeightConstraint.constant = height
}
/// recommended methods
/// Delegate method is called when the ad has been closed and you want to collaps the adContainer, or call a new ad
func visxAdViewClosed(visxAdView: VisxAdView) {
containerHeightConstraint.constant = 0
}
/// Delegate method is called when retrieving the ad content has failed for any reason, e.g. NoAd and provides a detail error message
func visxAdFailedWithError(visxAdView: VisxAdView, message: String, code: Int) {
print("didFailWithError \(message)")
containerHeightConstraint.constant = 0
}
}
Using Closures
adView?.callbackHandler?
.onInitialize { [weak self] visxAdView, effect in
self?.containerHeightConstraint.constant = visxAdView.frame.size.height
self?.adContainer.addSubview(visxAdView)
}
.onSizeChange{ [weak self] visxAdView, width, height in
self?.containerHeightConstraint.constant = height
}
.onClose{ [weak self] visxAdView in
self?.containerHeightConstraint.constant = 0
}
.onError { [weak self] visxAdView, visxError in
self?.containerHeightConstraint.constant = 0
}
In case of no ad or any other error visxAdFailedWithError
delegate or onError
callback will provide additional information via an error message and an error code. All potential error codes and their meanings are listed here.
Specifics for UITableView, UICollectionView
The basics for integrating the VisxAdView
in UITableView
or UICollectionView
are the same, but the order of when and where to add the AdView as a SubView is slightly changed.
Instead of an adContainer, you initiate a VisxTableViewCell
or VisxCollectionViewCell
to adjust its height directly, rather than using a NSLayoutConstraint
. After the size of the cell is changed, you need to reloadData()
of the UITableView
/ UICollectionView
to reflect the change in the UI.
Using Delegates
import VisxSDK
class MyTableViewController: UIViewController {
@IBOutlet weak var demoTableView: UITableView!
var adView: VisxAdView?
let adCellRow = 8
var cellHeight: CGFloat = 0
override func viewDidLoad() {
super.viewDidLoad()
adView = VisxAdView(adUnit: "123456",
adViewDelegate: self,
adSize: CGSize(width: 300, height: 250),
interstitial: Bool? = false)
adView?.load()
}
}
extension MyTableViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 16
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (indexPath.row == adCellRow && adView != nil) {
let adCell = demoTableView.dequeueVisxCell(for: indexPath, reuseIdentifier: "visxCell")
adCell.showAd(adView: adView!, tableView: demoTableView)
return adCell
} else {
let cell = demoTableView.dequeueReusableCell(withIdentifier: "demoInfeedCell", for: indexPath) as! DemoInfeedTableViewCell
cell.setupCell(indexPath.row)
return cell
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row == adCellRow {
return cellHeight
}
return 140
}
}
extension MyTableViewController: VisxAdViewDelegate {
func viewControllerForPresentingVisxAdView() -> UIViewController {
self
}
func visxAdViewDidInitialize(visxAdView: VisxAdView, effect: VisxPlacementEffect) {
cellHeight = visxAdView.frame.size.height
demoTableView.reloadData()
}
func visxAdViewSizeChange(visxAdView: VisxAdView, width: CGFloat, height: CGFloat) {
cellHeight = height
demoTableView.reloadData()
}
/// recommended
func visxAdViewClosed(visxAdView: VisxAdView) {
cellHeight = 0
demoTableView.reloadData()
}
func vvisxAdFailedWithError(visxAdView: VisxAdView, message: String, code: Int) {
print("didFailWithError \(message)")
cellHeight = 0
demoTableView.reloadData()
}
}
Using Closures
class MyTableViewController: DemoInfeedViewController {
var adView: VisxAdView?
let adCellRow = 8
var cellHeight: CGFloat = 0
override func viewDidLoad() {
super.viewDidLoad()
adView = VisxAdView(adUnit: "912262", adSize: CGSize(width: 300, height: 250))
adView?.callbackHandler?
.onInitialize { [weak self] visxAdView, effect in
self?.cellHeight = visxAdView.frame.size.height
self?.demoTableView.reloadData()
}
.onSizeChange{ [weak self] visxAdView, width, height in
self?.cellHeight = height
self?.demoTableView.reloadData()
}
/// recommended
.onClose{ [weak self] visxAdView in
self?.cellHeight = 0
self?.demoTableView.reloadData()
}
.onError { [weak self] visxAdView, visxError in
self?.cellHeight = 0
self?.demoTableView.reloadData()
}
adView?.load()
}
}
extension MyTableViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 16
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (indexPath.row == adCellRow && adView != nil) {
let adCell = demoTableView.dequeueVisxCell(for: indexPath, reuseIdentifier: "visxCell")
adCell.showAd(adView: adView!, tableView: demoTableView)
return adCell
} else {
let cell = demoTableView.dequeueReusableCell(withIdentifier: "demoInfeedCell", for: indexPath) as! DemoInfeedTableViewCell
cell.setupCell(indexPath.row)
return cell
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row == adCellRow {
return cellHeight
}
return 140
}
}
Providing the anchorFrame to VisxAdView
This step is optional, but highly recommended for improved performance and monetization.
Some YOC ad formats, such as the YOC Understitial Ad®, YOC Mystery Scroller® or YOC Branded Takeover require access to the UIScrollView
, UITableView
or UICollectionView
the VisxAdView
has been added as a SubView. This is needed to unfold scroll effects or determine their absolute boundaries for resizing.
In case of complex app layouts or whenever the VisxAdView
cannot access other UI elements you may need to provide the viewable part of the View, the VisxAdView
is located, manually.
extension VisxAdView {
/// Sets absolute position of the visible view where creative can be shown.
/// Note: Ad container is the UIView which is adding creative as a subview.
/// - Parameters:
/// - anchorX: Absolute x position of ad container in which creative is added.
/// - anchorY: Absolute top y position of UIScrollView/UITableView/UICollectionView.
/// - anchorWidth: Max width of the ad container.
/// - anchorHeight: Max height of the UIScrollView/UITableView/UICollectionView.
public func setAnchorFrame(with anchorX: Double, _ anchorY: Double, _ anchorWidth: Double, _ anchorHeight: Double) { /* .. */ }
}
Setting the anchorFrame manually is also useful in cases your UIScrollView
has static decorative UI elements that might overlap with the ad position.
extension MyScrollViewController: VisxAdViewDelegate {
func visxAdViewDidInitialize(visxAdView: VisxAdView, effect: VisxPlacementEffect) {
// Given myScrollView has a static label of 60px height floating on top
// and and the VisxAdView might not recognise it, we want to
// a) reduce the max height the ad might take by 60px
// b) push the top most position of the creative by 60px
// to ensure it will not overlap
let topLabelOffset = 60
let visibleFrame = self.view.convert(myScrollView.frame, to: nil)
visxAdView.setAnchorFrame(with: visibleFrame.origin.x,
visibleFrame.origin.y + topLabelOffset,
visibleFrame.width,
visibleFrame.height - topLabelOffset)
containerHeightConstraint.constant = visxAdView.frame.size.height
adContainer.addSubview(visxAdView)
}
Your inline placement is fully set up and ready for testing.
We advise you to share a build of your app with the YOC Service Team, to validate and fully test the integration together. Reach out to your contact at YOC to request test resources.