Skip to main content

Banners place

Starting with SDK version 1.26.0, banner display functionality has been added.

Initialization​

To embed the banner widget into your application, you must first initialize the SDK (if it has not been initialized previously with other functionality). SDK initialization is covered in the Basic initialization section.

Banners can be added as StoryView:

var bannersView: IASBannersView!

override func viewDidLoad() {
super.viewDidLoad()

bannersView = IASBannersView(placeID: <String> = "default",
appearance: <IASBannersAppearance> = .init(),
frame: <CGRect> = .zero)

view.addSubview(bannersView)

bannersView.create() // beginning of the widget's internal logic
}

After calling the create() method, the internal logic for opening a session and obtaining a list of banners will start. You can track the loading of banners in the bannersDidUpdated closure (see description below).

Please note

Prior to version 1.28.6, the first call to the create() method cached the data received from the server, and subsequent calls—even for new IASBannersView instances—retrieved data from the cache

Starting with version 1.28.6, the create() method always fetches the banner list from the server unless preload banners place has been called. Read more about preloading here read here

Customization​

To change the appearance of banners, use the IASBannersAppearance object:

struct IASBannersAppearance {
enum LayoutType: Hashable {
case horizontalCenterend, verticalList, tilesList
}
let layoutType: LayoutType // .horizontalCenterend (default), .verticalList, or .tilesList; since 1.29.9
let maxBannersCount: Int? // vertical and tiles lists: default 20; nil includes all available banners; since 1.29.9
let shouldLoop: Bool // if banner place is looped, default = true
let sideInset: CGFloat // which part (in dp) of other banners will be shown with current, default = 0.0
let leadingInset: CGFloat // setting the cast parameter for indentation (if not specified, it is taken from sideInset)
let trailingInset: CGFloat // setting the cast parameter for indentation (if not specified, it is taken from sideInset)
let interItemSpacing: CGFloat // distance (in dp) between banners, default = 8.0
let columnSpacing: CGFloat // tiles only: defaults to interItemSpacing; since 1.29.9
let rowSpacing: CGFloat // tiles only: defaults to interItemSpacing; since 1.29.9
let cornerRadius: CGFloat // radius (in dp) of banners, default = 16.0
}

Starting with SDK 1.29.9 (4370), IASBannersAppearance supports these layouts:

layoutTypeLayoutScrolling and height
.horizontalCenterendHorizontal carousel (default)Internal horizontal scrolling; set the widget height as before.
.verticalListOne-column vertical listInternal vertical scrolling; set a viewport height.
.tilesListTwo-column gridNo internal scrolling; the widget expands to its full content height inside an external scroll container.

The new initializer parameters have defaults: layoutType: .horizontalCenterend, maxBannersCount: 20, columnSpacing: nil, and rowSpacing: nil. Omitted grid gaps use interItemSpacing. Keeping the defaults preserves the carousel behavior from before 1.29.9: the banner limit and grid gaps do not affect the carousel.

In .verticalList and .tilesList, maxBannersCount limits the available banners in backend order: nil includes all available banners, 0 gives an empty list, and negative values are clamped to 0. columnSpacing and rowSpacing affect only .tilesList; negative or non-finite gap values are clamped to 0.

This object must be set when initializing the banner widget. Once set, it cannot be changed. If you need to change the appearance of the widget, you must create a new instance of it.

var bannersView: IASBannersView!

override func viewDidLoad() {
super.viewDidLoad()

let bannersAppearance = IASBannersAppearance(shouldLoop: false,
sideInset: 24.0,
interItemSpacing: 16.0,
cornerRadius: 8.0)

bannersView = IASBannersView(appearance: bannersAppearance)

view.addSubview(bannersView)

bannersView.create()
}

Preloading​

To make Banners place showing faster, you can preload banners data by placeID. Call InAppStory.shared.preloadBanners(placeID: String, complete: @escaping (Result<Bool, Error>) -> Void) and wait for completion.

Pay attention
Preloading is only possible after SDK initialization.

Basic preloading (for example, on a Splash Screen)​

InAppStory.shared.preloadBanners(placeID: <placeID>) { result in
switch result {
case .success:
// Data was loaded
break
case .failure:
// Loading error
break
}
}

Please note
After preloading a banner place, all instances of the IASBannersView object with a preloaded placeID will retrieve data only from the cache. To update the data in the list, you must either call the refresh() method or trigger the preload again.

Actions​

To track the actions of the banner widget, you need to subscribe to the closures:

  • bannersDidUpdated: <(_ isContent: <Bool>, _ count: <Int>, _ listHeight: <CGFloat>) -> Void> - called when the banner list is updated, for example, when first loaded, after calling create() or refresh() to update the list:
    • isContent: <Bool> - a parameter showing whether there is content in the widget;
    • count: <Int> - a parameter showing how many items are in the list (can be used for dot control navigation);
    • listHeight: <CGFloat> - carousel height calculated from its width and the largest banner; for .verticalList and .tilesList (since 1.29.9), the full content height;
  • onActionWith: (_ target: <String>) -> Void: called when an action occurs inside the banner, such as clicking a button with a link;
    • target: <String> - link obtained as a result of clicking a button in the banner;
  • bannersDidScroll: (_ index: <Int>) -> Void: called when the banner list is scrolled. This can be a swipe through the list, scrolling by an internal timer, or by external methods (described below);
    • index: <Int> - the index to which the banner list has scrolled (can be used for dot control navigation);
extension ViewController {
func observers() {
bannersView.bannersDidUpdated = { isContent, count, listHeight in
// banner list updated
// skeleton or preloader (if present) can be removed
// use listHeight for carousel/grid height, not the vertical list viewport
}

bannersView.onActionWith = { target in
// an action was performed
// here you can process the link
// that came in the target parameter
}

bannersView.bannersDidScroll = { index in
// list of banners has been updated
// dot control navigation can be updated
}
}
}

Vertical list​

Available since SDK 1.29.9 (4370). Use .verticalList to display banners in the SDK's own scrolling container. Give the widget a viewport height using UIKit constraints or SwiftUI's .frame(height:):

let appearance = IASBannersAppearance(
layoutType: .verticalList,
sideInset: 16,
interItemSpacing: 12,
maxBannersCount: 20
)

// UIKit: constrain this view to the desired viewport, then call create().
let banners = IASBannersView(placeID: "default", appearance: appearance)

// SwiftUI
IASBannersListView(id: "default", appearance: appearance)
.frame(height: 500)

The list preserves backend order after applying banner availability rules. maxBannersCount limits the displayed list, not concurrent downloads. It defaults to 20; set it to nil to include all available banners returned by the backend. Zero or a negative value produces an empty list.

Each banner's height is its available width divided by its own ratio, rounded to the nearest point. Banners may have different heights. sideInset, leadingInset, and trailingInset control horizontal padding; interItemSpacing separates rows. In this mode, the height reported by bannersDidUpdated / onUpdate is the full content height, so do not use it as the scrolling viewport height.

There is no pagination or automatic scrolling. shouldLoop is ignored for list navigation. showNext() and showPrevious() have no effect; use showBannerWith(index:) (SwiftUI: showWithIndex) to scroll to an explicit index.

Playback and loading rules for both list layouts are described below.

Two-column grid​

Available since SDK 1.29.9 (4370). Use .tilesList for a two-column grid inside a screen's existing scroll container. This layout has no internal scrolling and expands to the full content height.

let appearance = IASBannersAppearance(
layoutType: .tilesList,
sideInset: 16,
maxBannersCount: 20,
columnSpacing: 8,
rowSpacing: 12
)

// UIKit: add to an external UIScrollView's content container.
let banners = IASBannersView(placeID: "default", appearance: appearance)
// Constrain width and position; intrinsicContentSize supplies the grid height.
// For manual frames, measure the height with sizeThatFits(_:).
banners.create() // After setting up the view and initializing the SDK.

// SwiftUI (iOS 15+): the grid height updates automatically.
ScrollView {
IASBannersListView(id: "default", appearance: appearance)
.frame(maxWidth: .infinity)
}

Cell width is (containerWidth - leadingInset - trailingInset - columnSpacing) / 2. Each cell's height is its width divided by its own ratio, rounded to the nearest point. The next row starts below the tallest cell in the previous row, separated by rowSpacing. An odd final banner occupies one column. Leading/trailing placement respects RTL.

shouldLoop is ignored. showNext(), showPrevious(), and showBannerWith(index:) (SwiftUI: showWithIndex) have no effect in this layout; scroll the external container instead. bannersDidUpdated / onUpdate reports the full grid content height.

List playback and loading​

Since SDK 1.29.9 (4370), .verticalList and .tilesList share these rules:

  • Every banner with strictly more than 75% of its area visible plays simultaneously and muted. For a banner taller than the visible container, more than 75% of the container's available height must be occupied by that banner.
  • At 75% visibility or less, playback pauses. It resumes from the same position while the web context survives. After cell reuse, context reset, or deinitialization, it starts from the beginning.
  • A completed banner repeats itself while eligible. Repetition does not advance the list or add another view statistic. As with the carousel, view is recorded once per banner per list load.
  • Backgrounding the app pauses playback. A manual pause remains in effect during scrolling and background/foreground transitions until resume() is called.
  • Lists also pause while their input dialog or another SDK reader is displayed; playback eligibility is checked again after it closes.
  • Clicks remain enabled below the playback threshold. Ready banners appear independently; failed rows retain their space and offer an individual retry.

In .tilesList only, if the SDK cannot determine the visible viewport, all banners play and count as viewed. Banners that are known to be hidden do not use this fallback.

Both list layouts use a separate standard loading indicator per cell. Custom views from placeholderBannerView or placeholderView are used for the horizontal carousel and are not reused across list cells. See separate loading placeholders.

Pause and resume​

In SDK 1.29.9 (4370), the existing public IASBannersView.pause() / resume() methods and SwiftUI pause(_:) / resume(_:) modifiers also control playback in the new vertical and grid layouts. These methods and modifiers were already available for the carousel before 1.29.9. Call them on the main thread.

// UIKit
bannersView.pause()
bannersView.resume()

In SwiftUI, the modifiers provide closures that you can save and call later:

struct BannersScreen: View {
@State private var pauseBanners: (() -> Void)?
@State private var resumeBanners: (() -> Void)?

var body: some View {
VStack {
IASBannersListView(
id: "default",
appearance: IASBannersAppearance(layoutType: .verticalList)
)
.pause { pauseBanners = $0 }
.resume { resumeBanners = $0 }
.frame(height: 500)

Button("Pause") { pauseBanners?() }
Button("Resume") { resumeBanners?() }
}
}
}

In the vertical and grid layouts, pause() stops playback without disabling scrolling or clicks. resume() clears the manual pause and rechecks visibility; only eligible banners play while the app is active. Without manual calls, the SDK manages playback automatically. The persistent manual-pause rule described above applies to these two list layouts; the carousel retains its existing automatic visibility/foreground resume behavior.

To control the display and movement of banners, you can use methods to flip to the nearest ones or jump to a specific index:

  • showNext() - used to jump to the next banner in the list;
  • showPrevious() - used to go to the previous banner in the list;
  • showBannerWith(index: <Int>) - used to go to a specific banner by index (can be used for dot control navigation);
extension ViewController {
func nextButtonTouch() {
bannersView.showNext()
}

func previosButtonTouch() {
bannersView.showPrevious()
}

func dotControlTouch(with index: Int) {
bannersView.showBannerWith(index: index)
}
}

Similarly, there are methods for controlling playback in banners that control the internal page-turning timer.

  • pause() - stops the internal timer (the timer will restart upon any user interaction with the banner);
  • resume() - resumes the logic of the internal timers;
extension ViewController {
func disappearController() {
bannersView.pause()
}

func appearController() {
bannersView.resume()
}
}

Input fields and safe area​

Since SDK 1.29.9 (4370), banners support text, email, and telephone input fields. The SDK displays its standard input dialog unless you set a custom inputPresenter. Existing content without input fields needs no changes.

The SDK also supplies safe-area data to banner content, as it does for stories. Banners use zero top and bottom insets because they are embedded in the application's container; keep the container's constraints responsible for its position on screen. No new safe-area setting is required.

Events​

For banners, a new subobject IASBanner has been added to the IASEvent object (for more details, see the Events section).

  • IASEvent.IASBanner.bannersLoaded(placeID: String? = nil) - the list of banners has loaded, IASBannersView is ready to work (fires every time the list is loaded, and also on refresh).
    • placeID - banners place identifier;
  • IASEvent.IASBanner.show(bannerData: IASBannerData) - called when a banner is displayed on the screen.
    • bannerData - containing a brief description of the selected banner;
  • IASEvent.IASBanner.preloaded(placeID: String, banners: Array<IASBannerData>) - called when a banners is preloaded by place.
    • placeID - banners place identifier;
    • bannerData - containing a brief description of the selected banner;
  • IASEvent.IASBanner.widgetEvent(bannerData: IASBannerData, name: String, data: Dictionary<String, Any>?)
    • bannerData - containing a brief description of the selected banner;
    • name - name of widget;
    • data<Dictionary<String, Any>?> - activated widget data, detailed data fields;