# SDK Native - Custom Layout

## Native SDK — Custom Layouts

> **Prerequisite:** Familiar with the [Customizations](/ios/sdk-native/sdk-native-customizations.md) options.

When the default layout doesn't fit your design, you can provide a completely custom cell class. You control every pixel — the SDK only handles data fetching, impression tracking, and click handling.

***

### How It Works

You provide an implementation of `MCNativeAdRenderer` — a protocol with some mandatory methods plus some optional ones:

<table><thead><tr><th width="358">Method</th><th>What it does</th></tr></thead><tbody><tr><td><code>cellReuseIdentifier</code></td><td>A unique reuse identifier for your cell class</td></tr><tr><td><code>register(with:)</code></td><td>Registers your <code>UICollectionViewCell</code> subclass on the collection view</td></tr><tr><td><code>dequeueCell(collectionView:indexPath:)</code></td><td>Dequeues a cell using the reuse identifier</td></tr><tr><td><code>configure(cell:campaign:at:)</code></td><td>Binds campaign data to your cell — you decide what to show. Always called on the main thread.</td></tr><tr><td><code>itemSize(in:)</code></td><td>Returns the size of a single card</td></tr><tr><td><code>itemSize(in:for:)</code> <em>(optional)</em></td><td>Per-campaign size override. Default returns <code>itemSize(in:)</code>. Override when cells need different heights based on content (e.g. an "In Progress" badge that changes the card height).</td></tr><tr><td><code>lineSpacing</code> <em>(optional)</em></td><td>Spacing between consecutive items along the scroll axis. Default: 12pt.</td></tr></tbody></table>

The SDK handles everything else: fetching, scrolling, impression tracking, click handling.

Here we present three different custom layout examples that you can use as inspiration for your implementation. These are just examples — you can customize the layout however you'd like.

***

### Example A: Circular Thumbnails

This example creates a horizontal scroll of campaigns with circular images, promo badges, in-progress indicators, and a currency icon.

<figure><img src="/files/nHpQ1a5KiyzGr9VhhzN7" alt="" width="340"><figcaption></figcaption></figure>

#### Step A.1: Create the cell class

```swift
import UIKit
import MyChipsSdk

final class CircularCell: UICollectionViewCell {
    let thumbnailImageView = UIImageView()
    let nameLabel = UILabel()
    let currencyImageView = UIImageView()
    let rewardLabel = UILabel()
    let promoBadge = PaddedLabel()
    let progressBadge = PaddedLabel()

    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    required init?(coder: NSCoder) { fatalError() }

    private func setup() {
        contentView.clipsToBounds = false
        clipsToBounds = false

        // Circular thumbnail on a light grey background
        thumbnailImageView.contentMode = .scaleAspectFill
        thumbnailImageView.clipsToBounds = true
        thumbnailImageView.layer.cornerRadius = 70
        thumbnailImageView.backgroundColor = UIColor(white: 0.91, alpha: 1)
        thumbnailImageView.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(thumbnailImageView)

        nameLabel.font = .systemFont(ofSize: 14, weight: .semibold)
        // Horizontal cards sit directly on the host's background, so use
        // `.label` to stay readable in both light and dark system themes.
        nameLabel.textColor = .label
        nameLabel.textAlignment = .center
        nameLabel.numberOfLines = 2
        nameLabel.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(nameLabel)

        // Reward row: currency icon + reward label, centered
        let rewardRow = UIStackView()
        rewardRow.axis = .horizontal
        rewardRow.spacing = 4
        rewardRow.alignment = .center
        rewardRow.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(rewardRow)

        currencyImageView.contentMode = .scaleAspectFit
        currencyImageView.translatesAutoresizingMaskIntoConstraints = false
        rewardRow.addArrangedSubview(currencyImageView)

        rewardLabel.font = .systemFont(ofSize: 16, weight: .bold)
        rewardLabel.textColor = .label
        rewardRow.addArrangedSubview(rewardLabel)

        // Promo badge (top-left overlap)
        promoBadge.backgroundColor = UIColor(red: 0xD6/255, green: 0x2C/255, blue: 0x1F/255, alpha: 1)
        promoBadge.textColor = .white
        promoBadge.font = .systemFont(ofSize: 12, weight: .bold)
        promoBadge.layer.cornerRadius = 12
        promoBadge.layer.masksToBounds = true
        promoBadge.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(promoBadge)

        // In Progress badge (below reward)
        progressBadge.text = "In Progress"
        progressBadge.backgroundColor = UIColor(red: 0xE1/255, green: 0xE5/255, blue: 0xEB/255, alpha: 1)
        progressBadge.textColor = UIColor(red: 0x42/255, green: 0x4B/255, blue: 0x5A/255, alpha: 1)
        progressBadge.font = .systemFont(ofSize: 10, weight: .semibold)
        progressBadge.textAlignment = .center
        progressBadge.layer.cornerRadius = 9
        progressBadge.layer.masksToBounds = true
        progressBadge.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(progressBadge)

        NSLayoutConstraint.activate([
            thumbnailImageView.topAnchor.constraint(equalTo: contentView.topAnchor),
            thumbnailImageView.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
            thumbnailImageView.widthAnchor.constraint(equalToConstant: 140),
            thumbnailImageView.heightAnchor.constraint(equalToConstant: 140),

            nameLabel.topAnchor.constraint(equalTo: thumbnailImageView.bottomAnchor, constant: 8),
            nameLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
            nameLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),

            rewardRow.topAnchor.constraint(equalTo: nameLabel.bottomAnchor, constant: 6),
            rewardRow.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),

            currencyImageView.widthAnchor.constraint(equalToConstant: 16),
            currencyImageView.heightAnchor.constraint(equalToConstant: 16),

            promoBadge.topAnchor.constraint(equalTo: contentView.topAnchor, constant: -6),
            promoBadge.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: -6),
            promoBadge.heightAnchor.constraint(equalToConstant: 25),

            progressBadge.topAnchor.constraint(equalTo: rewardRow.bottomAnchor, constant: 6),
            progressBadge.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
            progressBadge.heightAnchor.constraint(equalToConstant: 18)
        ])
    }

    override func prepareForReuse() {
        super.prepareForReuse()
        thumbnailImageView.image = nil
        currencyImageView.image = nil
        nameLabel.text = nil
        rewardLabel.text = nil
        promoBadge.isHidden = true
        progressBadge.isHidden = true
    }
}
```

#### Step A.2: Set the renderer

Implement `MCNativeAdRenderer` and hand it to the ad view.

Remember to replace `https://my-cdn/my-coin-image.png` with your actual icon.

```swift
final class CircularRenderer: MCNativeAdRenderer {
    var cellReuseIdentifier: String { "CircularCell" }

    func register(with collectionView: UICollectionView) {
        collectionView.register(CircularCell.self, forCellWithReuseIdentifier: cellReuseIdentifier)
    }

    func dequeueCell(collectionView: UICollectionView, indexPath: IndexPath) -> UICollectionViewCell {
        collectionView.dequeueReusableCell(withReuseIdentifier: cellReuseIdentifier, for: indexPath)
    }

    func configure(cell: UICollectionViewCell, campaign: MCCampaign, at index: Int) {
        guard let cell = cell as? CircularCell else { return }
        cell.nameLabel.text = campaign.name
        cell.rewardLabel.text = MCDefaultAdRenderer.formatReward(campaign.totalConvertedValue)

        // Thumbnail (fallback to cover)
        let url = (campaign.creatives?.thumbnail?.isEmpty == false)
            ? campaign.creatives?.thumbnail
            : campaign.creatives?.cover
        MCOfferwallSDK.shared.loadImage(url: url, into: cell.thumbnailImageView)

        // Currency icon — replace with your URL
        MCOfferwallSDK.shared.loadImage(url: "https://my-cdn/my-coin-image.png",
                                        into: cell.currencyImageView)

        // Promo badge
        if campaign.promoRatio > 1.0 {
            cell.promoBadge.isHidden = false
            cell.promoBadge.text = MCDefaultAdRenderer.formatPromo(campaign.promoRatio)
        } else {
            cell.promoBadge.isHidden = true
        }

        // In Progress badge (hide when completed / closed)
        let status = campaign.progress?.status
        let hasProgress = status != nil
            && status != MCCampaignStatus.completed
            && status != MCCampaignStatus.closed
        cell.progressBadge.isHidden = !hasProgress
    }

    func itemSize(in collectionView: UICollectionView) -> CGSize {
        CGSize(width: 140, height: 240)
    }
}

// Usage
adView.setRenderer(CircularRenderer())
adView.load()
```

***

### Example B: Vertical Layout (small cards)

This example renders a vertical list of compact row cards. Each item shows a rounded thumbnail on the left, the title and an "In Progress" badge in the center, and the reward with currency icon on the right. The promo badge floats above the top-right corner of the card.

<figure><img src="/files/cquHNqYYFwUo3eH5yOim" alt="" width="308"><figcaption></figcaption></figure>

#### Step B.1: Create the cell class

```swift
final class SmallCampaignCell: UICollectionViewCell {
    let thumbnailImageView = UIImageView()
    let nameLabel = UILabel()
    let progressBadge = PaddedLabel()
    let currencyImageView = UIImageView()
    let rewardLabel = UILabel()
    let promoBadge = PaddedLabel()

    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    required init?(coder: NSCoder) { fatalError() }

    private func setup() {
        contentView.clipsToBounds = false
        clipsToBounds = false

        let card = UIView()
        card.backgroundColor = .white
        card.layer.cornerRadius = 12
        card.layer.borderColor = UIColor(white: 0.88, alpha: 1).cgColor
        card.layer.borderWidth = 1
        card.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(card)

        thumbnailImageView.contentMode = .scaleAspectFill
        thumbnailImageView.clipsToBounds = true
        thumbnailImageView.layer.cornerRadius = 12
        thumbnailImageView.translatesAutoresizingMaskIntoConstraints = false
        card.addSubview(thumbnailImageView)

        nameLabel.font = .systemFont(ofSize: 14, weight: .semibold)
        nameLabel.textColor = .black
        nameLabel.numberOfLines = 1

        progressBadge.text = "In Progress"
        progressBadge.backgroundColor = UIColor(red: 0xE1/255, green: 0xE5/255, blue: 0xEB/255, alpha: 1)
        progressBadge.textColor = UIColor(red: 0x42/255, green: 0x4B/255, blue: 0x5A/255, alpha: 1)
        progressBadge.font = .systemFont(ofSize: 10, weight: .semibold)
        progressBadge.layer.cornerRadius = 9
        progressBadge.layer.masksToBounds = true
        progressBadge.heightAnchor.constraint(equalToConstant: 18).isActive = true

        // Title + progress in a stack so the badge collapses when hidden
        let textStack = UIStackView(arrangedSubviews: [nameLabel, progressBadge])
        textStack.axis = .vertical
        textStack.alignment = .leading
        textStack.spacing = 4
        textStack.translatesAutoresizingMaskIntoConstraints = false
        card.addSubview(textStack)

        currencyImageView.contentMode = .scaleAspectFit
        currencyImageView.translatesAutoresizingMaskIntoConstraints = false
        card.addSubview(currencyImageView)

        rewardLabel.font = .systemFont(ofSize: 16, weight: .bold)
        rewardLabel.translatesAutoresizingMaskIntoConstraints = false
        card.addSubview(rewardLabel)

        promoBadge.backgroundColor = UIColor(red: 0xD6/255, green: 0x2C/255, blue: 0x1F/255, alpha: 1)
        promoBadge.textColor = .white
        promoBadge.font = .systemFont(ofSize: 12, weight: .bold)
        promoBadge.layer.cornerRadius = 10
        promoBadge.layer.masksToBounds = true
        promoBadge.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(promoBadge)

        NSLayoutConstraint.activate([
            card.topAnchor.constraint(equalTo: contentView.topAnchor),
            card.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
            card.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
            card.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),

            thumbnailImageView.leadingAnchor.constraint(equalTo: card.leadingAnchor, constant: 16),
            thumbnailImageView.centerYAnchor.constraint(equalTo: card.centerYAnchor),
            thumbnailImageView.widthAnchor.constraint(equalToConstant: 60),
            thumbnailImageView.heightAnchor.constraint(equalToConstant: 60),

            textStack.leadingAnchor.constraint(equalTo: thumbnailImageView.trailingAnchor, constant: 12),
            textStack.centerYAnchor.constraint(equalTo: card.centerYAnchor),
            textStack.trailingAnchor.constraint(lessThanOrEqualTo: currencyImageView.leadingAnchor, constant: -8),

            rewardLabel.trailingAnchor.constraint(equalTo: card.trailingAnchor, constant: -16),
            rewardLabel.centerYAnchor.constraint(equalTo: card.centerYAnchor),

            currencyImageView.trailingAnchor.constraint(equalTo: rewardLabel.leadingAnchor, constant: -4),
            currencyImageView.centerYAnchor.constraint(equalTo: card.centerYAnchor),
            currencyImageView.widthAnchor.constraint(equalToConstant: 18),
            currencyImageView.heightAnchor.constraint(equalToConstant: 18),

            promoBadge.topAnchor.constraint(equalTo: contentView.topAnchor, constant: -10),
            promoBadge.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -12),
            promoBadge.heightAnchor.constraint(equalToConstant: 20)
        ])
    }

    override func prepareForReuse() {
        super.prepareForReuse()
        thumbnailImageView.image = nil
        currencyImageView.image = nil
        nameLabel.text = nil
        rewardLabel.text = nil
        promoBadge.isHidden = true
        progressBadge.isHidden = true
    }
}
```

#### Step B.2: Wire the renderer

Set the orientation to vertical and provide an `MCNativeAdRenderer` that returns the cell above. Because the promo badge overlaps the top edge, override `lineSpacing` to add breathing room.

Also remember to replace `https://your.cdn/currency.png` with your actual currency icon.&#x20;

```swift
final class SmallRenderer: MCNativeAdRenderer {
    var cellReuseIdentifier: String { "SmallCampaignCell" }

    // Extra breathing room so the promo badge, which overlaps the top edge by
    // 10pt, doesn't bleed onto the previous card.
    var lineSpacing: CGFloat { 24 }

    func register(with collectionView: UICollectionView) {
        collectionView.register(SmallCampaignCell.self, forCellWithReuseIdentifier: cellReuseIdentifier)
    }

    func dequeueCell(collectionView: UICollectionView, indexPath: IndexPath) -> UICollectionViewCell {
        collectionView.dequeueReusableCell(withReuseIdentifier: cellReuseIdentifier, for: indexPath)
    }

    func configure(cell: UICollectionViewCell, campaign: MCCampaign, at index: Int) {
        guard let cell = cell as? SmallCampaignCell else { return }
        cell.nameLabel.text = campaign.name
        cell.rewardLabel.text = MCDefaultAdRenderer.formatReward(campaign.totalConvertedValue)

        let url = (campaign.creatives?.thumbnail?.isEmpty == false)
            ? campaign.creatives?.thumbnail
            : campaign.creatives?.cover
        MCOfferwallSDK.shared.loadImage(url: url, into: cell.thumbnailImageView)
        MCOfferwallSDK.shared.loadImage(url: "https://your.cdn/currency.png",
                                        into: cell.currencyImageView)

        if campaign.promoRatio > 1.0 {
            cell.promoBadge.isHidden = false
            cell.promoBadge.text = MCDefaultAdRenderer.formatPromo(campaign.promoRatio)
        } else {
            cell.promoBadge.isHidden = true
        }

        let status = campaign.progress?.status
        let hasProgress = status != nil
            && status != MCCampaignStatus.completed
            && status != MCCampaignStatus.closed
        cell.progressBadge.isHidden = !hasProgress
    }

    func itemSize(in collectionView: UICollectionView) -> CGSize {
        CGSize(width: collectionView.bounds.width - 32, height: 92)
    }
}

// Usage
adView.setOrientation(.vertical)
adView.setRenderer(SmallRenderer())
adView.load()
```

***

### Example C: Vertical Layout (big cards)

This example creates a vertical list of large cover cards. Each item shows a wide cover image at the top, and a full-width green reward pill at the bottom.

<figure><img src="/files/GfXwyvJFqSnUsnDQ9oLr" alt="" width="311"><figcaption></figcaption></figure>

#### Step C.1: Create the cell class

```swift
final class CoverCell: UICollectionViewCell {
    let coverImageView = UIImageView()
    let nameLabel = UILabel()
    let progressBadge = PaddedLabel()
    let rewardPill = UIView()
    let rewardLabel = UILabel()
    let currencyImageView = UIImageView()
    let promoBadge = PaddedLabel()

    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    required init?(coder: NSCoder) { fatalError() }

    private func setup() {
        let card = UIView()
        card.backgroundColor = .white
        card.layer.cornerRadius = 12
        card.layer.borderColor = UIColor(white: 0.88, alpha: 1).cgColor
        card.layer.borderWidth = 1
        card.clipsToBounds = true
        card.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(card)

        coverImageView.contentMode = .scaleAspectFill
        coverImageView.clipsToBounds = true
        coverImageView.translatesAutoresizingMaskIntoConstraints = false
        card.addSubview(coverImageView)

        nameLabel.font = .systemFont(ofSize: 16, weight: .bold)
        nameLabel.textColor = .black
        nameLabel.numberOfLines = 1

        progressBadge.text = "In Progress"
        progressBadge.backgroundColor = UIColor(red: 0xE1/255, green: 0xE5/255, blue: 0xEB/255, alpha: 1)
        progressBadge.textColor = UIColor(red: 0x42/255, green: 0x4B/255, blue: 0x5A/255, alpha: 1)
        progressBadge.font = .systemFont(ofSize: 10, weight: .semibold)
        progressBadge.layer.cornerRadius = 9
        progressBadge.layer.masksToBounds = true
        progressBadge.heightAnchor.constraint(equalToConstant: 18).isActive = true

        let textStack = UIStackView(arrangedSubviews: [nameLabel, progressBadge])
        textStack.axis = .vertical
        textStack.alignment = .leading
        textStack.spacing = 6
        textStack.translatesAutoresizingMaskIntoConstraints = false
        card.addSubview(textStack)

        rewardPill.backgroundColor = UIColor(red: 0x4C/255, green: 0xAF/255, blue: 0x50/255, alpha: 1)
        rewardPill.layer.cornerRadius = 23.5
        rewardPill.translatesAutoresizingMaskIntoConstraints = false
        card.addSubview(rewardPill)

        currencyImageView.contentMode = .scaleAspectFit
        currencyImageView.translatesAutoresizingMaskIntoConstraints = false
        rewardPill.addSubview(currencyImageView)

        rewardLabel.font = .systemFont(ofSize: 18, weight: .bold)
        rewardLabel.textColor = .white
        rewardLabel.translatesAutoresizingMaskIntoConstraints = false
        rewardPill.addSubview(rewardLabel)

        // Promo badge sits inside the cover, bottom-right
        promoBadge.backgroundColor = UIColor(red: 0xD6/255, green: 0x2C/255, blue: 0x1F/255, alpha: 1)
        promoBadge.textColor = .white
        promoBadge.font = .systemFont(ofSize: 12, weight: .bold)
        promoBadge.layer.cornerRadius = 12
        promoBadge.layer.masksToBounds = true
        promoBadge.layer.borderColor = UIColor.white.cgColor
        promoBadge.layer.borderWidth = 2
        promoBadge.translatesAutoresizingMaskIntoConstraints = false
        coverImageView.addSubview(promoBadge)
        coverImageView.isUserInteractionEnabled = true

        NSLayoutConstraint.activate([
            card.topAnchor.constraint(equalTo: contentView.topAnchor),
            card.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
            card.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
            card.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),

            coverImageView.topAnchor.constraint(equalTo: card.topAnchor),
            coverImageView.leadingAnchor.constraint(equalTo: card.leadingAnchor),
            coverImageView.trailingAnchor.constraint(equalTo: card.trailingAnchor),
            coverImageView.heightAnchor.constraint(equalToConstant: 172),

            textStack.topAnchor.constraint(equalTo: coverImageView.bottomAnchor, constant: 10),
            textStack.leadingAnchor.constraint(equalTo: card.leadingAnchor, constant: 12),
            textStack.trailingAnchor.constraint(equalTo: card.trailingAnchor, constant: -12),

            rewardPill.topAnchor.constraint(equalTo: textStack.bottomAnchor, constant: 10),
            rewardPill.bottomAnchor.constraint(lessThanOrEqualTo: card.bottomAnchor, constant: -12),
            rewardPill.leadingAnchor.constraint(equalTo: card.leadingAnchor, constant: 12),
            rewardPill.trailingAnchor.constraint(equalTo: card.trailingAnchor, constant: -12),
            rewardPill.heightAnchor.constraint(equalToConstant: 47),

            currencyImageView.leadingAnchor.constraint(equalTo: rewardPill.leadingAnchor, constant: 16),
            currencyImageView.centerYAnchor.constraint(equalTo: rewardPill.centerYAnchor),
            currencyImageView.widthAnchor.constraint(equalToConstant: 20),
            currencyImageView.heightAnchor.constraint(equalToConstant: 20),

            rewardLabel.leadingAnchor.constraint(equalTo: currencyImageView.trailingAnchor, constant: 8),
            rewardLabel.centerYAnchor.constraint(equalTo: rewardPill.centerYAnchor),

            promoBadge.bottomAnchor.constraint(equalTo: coverImageView.bottomAnchor, constant: -12),
            promoBadge.trailingAnchor.constraint(equalTo: coverImageView.trailingAnchor, constant: -12),
            promoBadge.heightAnchor.constraint(equalToConstant: 25)
        ])
    }

    override func prepareForReuse() {
        super.prepareForReuse()
        coverImageView.image = nil
        currencyImageView.image = nil
        nameLabel.text = nil
        rewardLabel.text = nil
        promoBadge.isHidden = true
        progressBadge.isHidden = true
    }
}
```

#### Step C.2: Wire the renderer

The cell height changes depending on whether the "In Progress" badge is shown, so override `itemSize(in:for:)` to return a per-campaign size.

Also remember to replace `https://your.cdn/currency.png` with your actual currency icon.&#x20;

```swift
final class CoverRenderer: MCNativeAdRenderer {
    var cellReuseIdentifier: String { "CoverCell" }

    func register(with collectionView: UICollectionView) {
        collectionView.register(CoverCell.self, forCellWithReuseIdentifier: cellReuseIdentifier)
    }

    func dequeueCell(collectionView: UICollectionView, indexPath: IndexPath) -> UICollectionViewCell {
        collectionView.dequeueReusableCell(withReuseIdentifier: cellReuseIdentifier, for: indexPath)
    }

    func configure(cell: UICollectionViewCell, campaign: MCCampaign, at index: Int) {
        guard let cell = cell as? CoverCell else { return }

        // Cover (fallback to thumbnail)
        let coverUrl = (campaign.creatives?.cover?.isEmpty == false)
            ? campaign.creatives?.cover
            : campaign.creatives?.thumbnail
        MCOfferwallSDK.shared.loadImage(url: coverUrl, into: cell.coverImageView)
        MCOfferwallSDK.shared.loadImage(url: "https://your.cdn/currency.png",
                                        into: cell.currencyImageView)

        cell.nameLabel.text = campaign.name
        cell.rewardLabel.text = MCDefaultAdRenderer.formatReward(campaign.totalConvertedValue)

        if campaign.promoRatio > 1.0 {
            cell.promoBadge.isHidden = false
            cell.promoBadge.text = MCDefaultAdRenderer.formatPromo(campaign.promoRatio)
        } else {
            cell.promoBadge.isHidden = true
        }

        let status = campaign.progress?.status
        let hasProgress = status != nil
            && status != MCCampaignStatus.completed
            && status != MCCampaignStatus.closed
        cell.progressBadge.isHidden = !hasProgress
    }

    func itemSize(in collectionView: UICollectionView) -> CGSize {
        CGSize(width: collectionView.bounds.width - 32, height: 300)
    }

    func itemSize(in collectionView: UICollectionView, for campaign: MCCampaign) -> CGSize {
        let status = campaign.progress?.status
        let hasProgress = status != nil
            && status != MCCampaignStatus.completed
            && status != MCCampaignStatus.closed
        return CGSize(width: collectionView.bounds.width - 32,
                      height: hasProgress ? 300 : 272)
    }
}

// Usage
adView.setOrientation(.vertical)
adView.setRenderer(CoverRenderer())
adView.load()
```

***

### Key Points for Custom Layouts

#### Click Handling

The SDK wires click handlers **automatically** — tapping any item opens the campaign's detail page. You can override this with `adView.onCampaignClick`.

#### Image Loading

Use the SDK's built-in image loader in your renderer:

```swift
MCOfferwallSDK.shared.loadImage(url: url, into: imageView)
```

It handles background downloading, `UIImage` decoding, and cell recycling. No external library needed.

#### Cell Recycling Tip

Always reset images and hidden-state views in `prepareForReuse()`. The SDK does not restrict your cell class in any way — it just dequeues and calls `configure(cell:campaign:at:)`.

***

### Next Steps

* Need the full data reference? See [Data Reference →](/ios/sdk-native/sdk-native-data-reference.md)
* Need simpler tweaks? See [Simple Customization →](/ios/sdk-native/sdk-native-customizations.md)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.mychips.io/ios/sdk-native/sdk-native-custom-layout.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
