Search code examples
iosobjective-ciphoneuiviewcontrolleruicontainerview

Adding Ad Banners in multiple UIViewcontroller app


I have built an app like 2 years ago using story board. The root view controller of the app is UINavigation controller and the root view controller loads view controllers as per user interaction. From root view controller, depending on user action 8 other view controllers can be presented in UINavigation.

Now I would to include banner ads to that app.The problem is since I originally implemented the app using story board and viewcontrollers, I am wondering where to add the code for ad banner.

I did some research and it looks like the likely option is to 1. Add a UIViewcontroller as a root view controller 2. Add a ContainerView to the UIViewcontroller 3. Make Exiting navigation controller as embedded segue from the container view 4. Add the ad banner (Want the ad banner at the bottom of the screen).

In Other words

Existing

--> UINavigationController -->(segue)-->LoginController-->(segue)-->MainpageController-->

Planned Modification

--> UIViewController-->(has)-->Containerview-->(embed segue)-->UINavigationController -->(segue)-->LoginController-->(segue)-->MainpageController-->

What I like to know is, is this the best approach to implement ad banners in my case? or can I have ad banner view is each and every view controller which may be presented in navigation controller?

Thanks


Solution

  • Apple's BannerView sample app seems to cover this. It isn't a storyboard app but the basic rule should apply. There is a single VC that has the ad view as well as your app's view.

    I wrote a blog post about it here: http://www.notthepainter.com/iad-admob-integration-with-a-dynamic-uiview/

    Note, the blog post is about iAd and ad mob integration, but the same concepts should apply. You don't need to use ad integration to accomplish this at all. You can really ignore the rest of this answer and just see how Apple does it with the BannerView sample app.

    Here's the blog posting:

    ’ve released 2 apps both with iAds. I used Apple’s BannerView sample code to implement this. Basically, in your delegate you don’t set root to your expected root UIViewController but rather you set root to a BannerView which contains your real root. When an iAd is available, your main view shrinks and the iAd is displayed at the bottom. When an ad isn’t available, your view expands to its “normal” size.

    This worked very well in testing so I released both apps to the app store. However, when I first downloaded the versions from the store I was quite surprised to see no ads ever. It turns out that at least right now, iAd had a pretty horrible fill rate. So I wanted to show another ad when an iAd wasn’t available.

    I found LARSAdController, an open source project by larsacus on GitHub. He makes ad integration very easy except for one thing. When you go down his quick development route you get the ads covering your view, it doesn’t shrink to accommodate the ad. This is a completely reasonable design decision, just not one I wanted.

    So I decided to modify Apple’s BannerView to use LARSAdController. It was pretty easy.

    The first thing you do is remove iAd from BannerView’s .h file and ad in the LARS TOLAdViewController class.

    #import "TOLAdViewController.h"
    
    #define KVO_AD_VISIBLE @"KVO_AD_VISIBLE"
    
    @interface BannerViewController : TOLAdViewController
    

    (Just ignore the KVO_AD_VISIBLE define for now, I’ll cover that later.) In the .m file also remove iAd and make these changes:

    @implementation BannerViewController {
        UIView *_bannerView;
        UIViewController *_contentController;
        BOOL isLoaded;
    }
    

    We changed _bannerView from an ADBannerView into a plain old UIVIew. ADBannerView also has a bannerLoaded property which we’ll have to replace with our isLoaded boolean. initWithContentViewController is also easy to modify.

    // IAPHelper *sharedInstance = [//IAPHelper sharedInstance];
    //if ([sharedInstance showBannerAds]) {
    if (YES) {
        _bannerView = [[UIView alloc] initWithFrame:CGRectZero];
    } else {
        _bannerView = nil;      // not showing ads since the user has upgraded
    }
    

    Notice the commented out section. If you are using in-app purchases to transform an ad supported version into an ad free version you can do that right there.

    At the end of the method well use Key-Value-Observing (KVO) to watch LARS and see when an ad is served or removed. (I’ll probably cover KVO in a future blog entry.)

    [[LARSAdController sharedManager] addObserver:self
                                   forKeyPath:kLARSAdObserverKeyPathIsAdVisible
                                      options:0
                                      context:KVO_AD_VISIBLE]
    

    And the observing code:

    - (void)observeValueForKeyPath:(NSString *)keyPath
                          ofObject:(id)object
                            change:(NSDictionary *)change
                           context:(void *)context;
    {
        if(context == KVO_AD_VISIBLE) {
            NSNumber *isVisible = [object valueForKey:kLARSAdObserverKeyPathIsAdVisible];
    
            if ([isVisible boolValue]) {
                _bannerView.frame = [[LARSAdController sharedManager] containerView].frame;
                isLoaded = YES;
            } else {
                isLoaded = NO;
            }
        }
    
        [self.view setNeedsLayout];
        [self.view layoutIfNeeded];
    }
    

    We save the frame of the new ad and also update the isLoaded variable. (Originally thought I would need to call setNeedsLayout and layoutIfNeeded but in practice I didn’t.) The mods to viewDidLayoutSubviews were also pretty straightforward. The only odd part was removing the references to ADBannerContentSizeIdentifierPortrait and ADBannerContentSizeIdentifierLandscape and just replacing that all with a single line:

    bannerFrame.size = [_bannerView sizeThatFits:contentFrame.size];
    

    And a few lines later you use the new isLoaded variable

    if (isLoaded) {
    

    Don’t forget to clean up your observer in dealloc:

    -(void) dealloc;
    {
        [[LARSAdController sharedManager]  removeObserver:self forKeyPath:kLARSAdObserverKeyPathIsAdVisible];
    }
    

    All that remains is to tell LARS about your ads in your app delegate:

    [[LARSAdController sharedManager] registerAdClass:[TOLAdAdapterGoogleAds class] withPublisherId:@"a14e55c99c24b43"];
    [[LARSAdController sharedManager] registerAdClass:[TOLAdAdapteriAds class]];
    
    LARSBannerViewController *root = [[LARSBannerViewController alloc] initWithNibName:@"LARSBannerViewController" bundle:nil];
    
    _bannerViewController = [[BannerViewController alloc] initWithContentViewController:root];
    
    [self.window setRootViewController:_bannerViewController];
    

    And that’s it. Now your app should show iAD or AdMob ads and your view will shrink to accommodate them.

    Of course there’s a bug, I don’t know if this is in AdMob server or in LARS but when you are in Landscape mode on an iPhone the ad’s size and the reported size are different leaving a black bar at the bottom of the screen. I’ve pinged larsacus about it and will update this post when I know more.