11

I'm trying to add some banner ads randomly inside my collectionView.

Each collectionView cell would be a basic image (black square here to make things easier) populated dynamically from an array (let's say it's a really long array and call it "longDataArray") that I would get from the web.

I could manage to add some banner ads to my collectionView but the problem is that it's breaking the order of my longDataArray. For example, just for testing when I'm adding an ad banner at indexPath 6, then the ad banner correctly shows up at indexPath 6, and I'm able to manage the width change for the cell, but the image corresponding at the indexPath 6 for my longDataArray would obviously never appears.

I also could do it spliting my longDataArray in two, and then playing with the sections : section 0 = firstPartOfArray, section 1 = ad banner, and section 2 = secondPartOfArray. But this takes a lot of effort creating differents arrays and sections, just to add only one ad banner, and it's obviously not what I'm looking for.

So my question is, how would you add banner ad within your collectionView (only one section), but keeping the indexPath logic ?

I googled a lot about that, and was surprised that I could not come up with any solution for this problem.

Do you guys have any idea ?

Thanks!

screenshot

Caleb
  • 124,013
  • 19
  • 183
  • 272
  • Are you using a table view or a collection view? Your title doesn't match the content. – Caleb Mar 28 '17 at 06:47
  • Sorry I mistyped, I meant a collectionView but the concept remains essentially the same regardless of whether you use a collectionView or a tableView, I'd like the ad banner to appear within the collectionView (or tableView) randomly (not only the placement of the banner, but also the number of banners) without breaking the logic of the indexPath –  Mar 28 '17 at 08:42
  • I would not create another section for the Ad, but rather insert it between the actual data in the array and adding some sort of flag to identify it (for example, `theActualDataForACell[@"is_ad"] = @YES;` and then `if ( [dataForCell[@"is_ad"] boolValue] ) { /* this is an ad */ }`, you can define the size for a specific cell in the `collectionView:layout:sizeForItemAtIndexPath:` method from the `UICollection​View​Delegate​Flow​Layout` protocol... or you can just create another cell prototype and dequeue the Ad one when appropiate. – Alejandro Iván Apr 04 '17 at 15:00
  • same here, did you find an elegant solution? – armnotstrong Jan 18 '18 at 03:00
  • @armnotstrong I think that AlejandroIván solution is easy and the best one to use. You just add a special object at random place (could be need to be at least a multiple of 3 index because of the layout shown, but that then should be easy. – Larme Jan 22 '18 at 15:50
  • @jellyfish6 try like when you are getting data and load to array at that time in your data model add one flag that shows the data type and when you are adding ad manually at that time make data true \ – Ravi Panchal Jan 23 '18 at 05:09

4 Answers4

4

For UICollectionView you have to two custom UICollectionView

  1. Cell1 is for the Imageview.
  2. Cell2 is for the the banner ad.

In cellForItem

if (indexPath.item == 6){
    // dequeue your cell2 here
    return cell2
}
else{
    // dequeue your cell1 here
    return cell1
}

Implement UICollection​View​Delegate​Flow​Layout and use like this

func collectionView(_ collectionView: UICollectionView, 
                      layout collectionViewLayout: UICollectionViewLayout, 
               sizeForItemAt indexPath: IndexPath) -> CGSize{

  if (indexPath.item == 6){
      return CGSizeMake(60,60)
  }
  else{
      return CGSizeMake([[UIScreen mainScreen] bounds].size.width, 60.0)
  }

}

To display ad in your app, you AdMob

dahiya_boy
  • 9,298
  • 1
  • 30
  • 51
  • Thanks for your reply @agent_stack, However, the problem if I build it like that is that I would have to split my data, which is not something I want (my data will grow dynamically and will be very big) –  Mar 28 '17 at 06:51
  • I'm looking for a solution that would not alter my data and the indexPath logic, if you have any idea ? –  Mar 28 '17 at 06:51
  • @jellyfish6 How actually your UI appears can you show me so that I can suggest accordingly. Please describe which type of data you have and what you are showing in collection view – dahiya_boy Mar 28 '17 at 06:52
  • Thanks again for your fast reply ! On this screen, users will be able to see all the pics shared by their friends. So basically my dataArray will consist in an array of structures, my dataArray = [PhotoStruct] and each PhotoStruct is composed of a photoImageURL, a photoAuthor, and a photoTime that I will grab and use to populate the collectionView. –  Mar 28 '17 at 07:02
  • Each collection cell will consist in the photo image (full cell size), the author label at the bottom and the time label at the top. –  Mar 28 '17 at 07:03
  • unfortunately this doesn't work, because the content of the dataArray[6] would never appear on screen, it would get replaced by the ad banner instead –  Mar 28 '17 at 07:23
  • @jellyfish6 No bro you need to right `array[indexPath.item - 1]` for later cells. – dahiya_boy Mar 28 '17 at 07:30
  • The problem is that the ad banners should be displayed randomly (the placement of the ad but also the number of them) so I won't be able to know directly the number of ads that are displayed, so how would I be able to adjust the indexPath in these conditions ? –  Mar 28 '17 at 07:50
  • @jellyfish6 So you are saying every time `VC` is appeared the whole colletionview get randomnised. You won't thing it seems to be little bit awkward . – dahiya_boy Mar 28 '17 at 08:41
  • not the whole collectionView, but only the ad banners yes ! I'd like to get a random placement (dynamic) and a random number of banners in the collectionView (let's say we want 10% ad filling) –  Mar 28 '17 at 08:53
  • @jellyfish6 then you need count variable. and your array will be like this `array[indexPath.item - count]` . Count is the total number of banned is showed on the screen. – dahiya_boy Mar 28 '17 at 08:55
3

Hi I have created a collectionView inside the table cell with same requirement, You can check my code.

//
//  ViewController.swift
//  DemoApp
//
//  Created by Mahesh Kumar on 09/01/18.
//  Copyright © 2018 Mahesh Kumar. All rights reserved.
import UIKit

class TableCell : UITableViewCell{
    @IBOutlet weak var collVw: UICollectionView!
    @IBOutlet weak var categoryName: UILabel!
}

class ViewController: UIViewController, UICollectionViewDelegate,UICollectionViewDataSource , UICollectionViewDelegateFlowLayout , UITableViewDelegate,UITableViewDataSource {

    var categories_array = ["Home","Helth","New","Home1","Home2","Home3","Home4","Home5","Home6","Home7","Home8","Home9","Home11","Home12","Home13","Home14","Home15"]

    //Mark
    var sectionArray = NSMutableArray()


    @IBOutlet weak var tableVw: UITableView!
    override func viewDidLoad() {
        super.viewDidLoad()

        //Mark
        var sectionCount = 0
        var mainIndex = 0

        let section = categories_array.count % 4
        if(section == 0){
            sectionCount =  categories_array.count/4
        }
        else{
             sectionCount =  categories_array.count/4 + 1
        }
        //Mark
        for _ in 0...sectionCount {
            let rowsData = NSMutableArray()
            var j = 0
            while  j<4{
                if(mainIndex == categories_array.count){
                    break
                }
                rowsData.add(categories_array[mainIndex])
                j = j + 1
                mainIndex = mainIndex + 1

            }
          sectionArray.add(rowsData)


        }

        tableVw.reloadData()

        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 3
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableVw.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as? TableCell

        if(indexPath.row == 1){
            cell?.categoryName.text = "Top Redeemed"
        }
        else if(indexPath.row == 2){
            cell?.categoryName.text = "Categories"
        }

        cell?.collVw.tag  = indexPath.row
        cell?.collVw.delegate = self
        cell?.collVw.dataSource = self
        cell?.collVw.reloadData()

        return cell!
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {

        return 400
    }


    func numberOfSections(in collectionView: UICollectionView) -> Int {
        if(collectionView.tag == 0){
              return 1
        }
        else if(collectionView.tag == 1){
            if(categories_array.count > 4){
            if(categories_array.count % 4 == 0){
                return categories_array.count/4
            }
            else{
                return (categories_array.count/4) + 1
            }
            }
            else{
                return 1
            }

        }
        else{
            return 10
        }


    }
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
        cell.backgroundColor = UIColor.green
        if let lbl = cell.viewWithTag(1) as? UILabel{
            lbl.text = "\(indexPath.row)"
        }
        return cell

    }
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
    {
        if(collectionView.tag == 0){
             return CGSize.init(width:  collectionView.frame.width - 10, height: collectionView.frame.height)
        }
        else if(collectionView.tag == 1){
            return CGSize.init(width:  (collectionView.frame.width)/2 - 5.5, height: collectionView.frame.height/2 - 0.5)

        }
       else {
            return CGSize.init(width:  collectionView.frame.width/3 - 4, height: collectionView.frame.height/2 - 0.5)
        }

    }


    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        if(collectionView.tag == 0){
            return 10
        }
        else{
            return 1
        }
    }

   func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets
   {
       return UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
   }




    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        if(collectionView.tag == 0){
           return 10
        }
         else if(collectionView.tag == 1){
            return ((sectionArray[section] as? NSMutableArray)?.count)!
        }
         else{
             return 6
        }

    }

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        print("coll vw",indexPath.row)
        print(indexPath.section)

        //Mark
        if(collectionView.tag == 1){
            print(collectionView.tag)
            let array = sectionArray[indexPath.section] as? NSMutableArray
            print(array![indexPath.row])
        }

    }


}
Mahesh kumar
  • 278
  • 4
  • 15
  • 1
    Please add this code it creates a three section one is Ads banner, second and third is categories and user can scroll horizontal and vertical . If it worked please accept my answer. – Mahesh kumar Jan 19 '18 at 05:00
2

So my question is, how would you add banner ad within your collectionView (only one section), but keeping the indexPath logic ?

You just need to adjust the index path to account for the ads. For example, let's say that you want every 15th cell to contain an ad. Let's use 1-based arithmetic here to make the math intuitive. Cells 1-14 will just get their regular content, cell 15 will have an ad, cells 16-29 will get the content for items 15-28, cell 30 will get another ad, and so on. So, your -collectionView:cellForItemAtIndexPath: method would need to figure out whether the index path refers to an ad cell (in which case the 1-based item number is evenly divisible by 15) or a content cell (every other cell). In the latter case, it'd also need to adjust the item number to get the right content.

NSInteger item = indexPath.item + 1;   // switch to 1-based numbering
if (item % 15 == 0) {
    // we have an ad cell, so return a cell configured with an ad
}
else {
    item = item - (item / 15); // subtract the ad cells
    item -= 1;                 // switch back to 0-based indexes
    // return a cell configured with the data at index `item`
}

You'd also have to do corresponding calculations in other methods that deal with cells, such as -collectionView:numberOfItemsInSection:. For that reason, it'd probably be a good idea to write some utility methods that would make the adjustments.

Caleb
  • 124,013
  • 19
  • 183
  • 272
  • Hi @Caleb and thanks for your detailed answer ! I had not thought about that and yet, it makes a lot of sense ! Using this I can keep track of what is my original indexPath and fetch the data accordingly. However, how would you make it work in the case of a random ad placement (not always every 15th cell) like I asked in my question ? How would you keep track of that randomness ? Could you please shed some light on this –  Mar 28 '17 at 07:19
  • If you place the ads randomly then you can't compute the indexes using a formula. I think in that case I'd keep a list of the ad positions and just use that list to calculate the difference. For example, if you have a list of ad positions like `[13, 27, 43, 58, 71]`, then every time you populate a cell you need to look at the list and figure out how many ads there are before that cell. If you're populating cell #30, you look at the list and see that there are two ads < 30, so you adjust the index into your data by 2. – Caleb Mar 28 '17 at 14:30
  • Another approach: insert placeholders for ads into your data. Just ad a new kind of item to the list of items that you're managing, and have your code ignore those items for most purposes. That avoids doing math to adjust the item indices, but might complicate other parts of your code. – Caleb Mar 28 '17 at 14:33
2

We succeeded that developed an example for with Ersin for this issue.

You can check it out here.

https://github.com/Cemoo/WaterFlowLayout

Cemal BAYRI
  • 401
  • 5
  • 12