Banner

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).

  1. Importing VisxSDK to your UIViewController
  2. Initializing VisxAdView for an ad request
  3. Rendering your ad into your AdContainer
  4. Specifics for UITableView, UICollectionView
  5. 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.

Image alt

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.