11

Is it possible with a standard UINavigationController-rooted app, to have a single ADBannerView visible at the bottom of the screen, below the view hierarchy? That is, without modifying each view-controller/view that can be pushed to the root UINavigationController, can I have a global ADBannerView be visible?

I'm not sure how to set this up, either in IB or in code. Help?

I see similar questions with vague answers. I'm looking for a concrete example.

bentford
  • 33,038
  • 7
  • 61
  • 57
TomSwift
  • 39,369
  • 12
  • 121
  • 149

4 Answers4

10

EDIT: The better way to do this in iOS5+ is likely to use view controller containment. That is, make a root controller that contains your ad and application controller (nav, tab, etc.).

I figured out a way to do this. Here is what I did:

For my first attempt I created a new view controller called AdBannerController. For its view I created a full-screen view and two subviews. The first subview (contentView) is for regular content, the second is the AdBannerView. I used an instance of this view controller as the view controller associated with the app window ( [window addSubview: adBannerController.view] ). Then I added my UINavigationController.view as a subview of adBannerController.view: [adBannerController.contentView addSubview: navigationController.view].

This mostly worked except that viewcontrollers pushed to the UINavigationController never got their will/did-load/unload methods called. Shucks. I read in a few places that this is a symptom of the UINavigationController view not being a direct descendant of the app window.

For my second attempt I took the same AdBannerController and derived it from UINavigationController. This time, I did the following in loadView:

- (void)loadView
{
    [super loadView];

    _contentView = [self.view retain];

    self.view = [[[UIView alloc] initWithFrame: _contentView.frame] autorelease];

    [self.view addSubview: _contentView];

    _adView = [[ADBannerView alloc] initWithFrame: CGRectMake(0, _contentView.bounds.size.height, 320, 50)];
    _adView.currentContentSizeIdentifier = ADBannerContentSizeIdentifier320x50;
    _adView.delegate = self;

    [self.view addSubview: _adView]; 

    /* for visual debugging of view layout
    [[_mainView layer] setCornerRadius: 6.0];
    [[_mainView layer] setMasksToBounds: YES];
    [[_mainView layer] setBorderWidth: 1.5];
    [[_mainView layer] setBorderColor: [[UIColor grayColor] CGColor]];  
     */
}

Notice what happens - I let the superclass UINavigationController construct its regular "content" view, but I swap it out and replace it with my own view which is a container for both the content and ad views.

This works pretty well. I'm also using three20 and there were a few things required to make this work with that setup, but not too bad.

I hope this helps someone!

TomSwift
  • 39,369
  • 12
  • 121
  • 149
  • Very helpful, I'm trying this now. – bentford Sep 17 '10 at 10:24
  • 1
    i implemented something like this but had difficulties in views where I am using a UISearchBar as the tableViewHeader, as well as in views where the navBar is transparent – coneybeare Sep 25 '10 at 19:58
  • @Paul de Lange - This hack is no longer necessary in iOS5. Just make yourself a container-view-controller for the root level. – TomSwift Jun 14 '12 at 17:42
  • @TomSwift - Thanks for the info. Production code not targeting iOS 4 is still a long way away for us :( – Paul de Lange Jun 15 '12 at 07:37
  • @Paul de Lange - It shouldn't be that far off; didn't Apple say that 80% of users are on iOS5 as of June, 2012? – TomSwift Jun 15 '12 at 15:02
  • @TomSwift - Yes but 20% less users seems to be a big number when talking to marketing people... Do you have any references to that figure you are quoting? – Paul de Lange Jun 15 '12 at 15:36
  • @Paul de Lange - agreed. I believe Apple stated this during the WWDC 2012 keynote, with a slide. Someone else please correct me if I'm mistaken. – TomSwift Jun 15 '12 at 15:46
3

In Apple's dev sample code the iAdSuite project contents projects that have this done for you. Highly recommended.

fearmint
  • 5,276
  • 2
  • 33
  • 45
  • 1
    Yeah, but it doesn't do what the OP is asking for -- instead, the ad in Apple's sample slides in with the subviews, and back out again when you use the "Back" button. – Joe Strout Jun 08 '11 at 16:22
  • Of course, if the view controller that manages the ads is pushed onto the stack and subsequently removed. I would replace the view controller that the app delegate and window use with the ad manager, then add the view hierarchy under that. – fearmint Jun 08 '11 at 18:13
2

In my root view controller (w/ ADBannerViewDelegate) I setup my banner by adding it to the nav controller view, which keeps it on top at all times:

banner = [[ADBannerView alloc] init];
banner.delegate = self;
banner.frame = CGRectMake(0.0, 430.0, banner.frame.size.width, banner.frame.size.height);
[self.navigationController.view addSubview:banner];

Note you will have to comment out layoutAnimated in delegate method bannerViewDidLoadAd as it will try to move the ad view up:

- (void)bannerViewDidLoadAd:(ADBannerView *)banner
{
    //[self layoutAnimated:YES];
}
mutable2112
  • 439
  • 4
  • 15
0

I adapted the approach suggested in the iAdSuite given here

http://developer.apple.com/library/ios/#samplecode/iAdSuite/Introduction/Intro.html

I downloaded the code and focused on the 'tab' example. I copied over the BannerViewController.h/.m as is into my project.

I created all my views in the usual way with the storyboard approach. However, in my AppDelegate class I then accessed the already built tab bar - containing all the storyboard built viewControllers.

The AppDelegate class implements the TabBarControllerDelegate protocol:

@interfaceAppDelegate : UIResponder <UITabBarControllerDelegate, UIApplicationDelegate>

The AppDelegate implementation didFinishLaunchingWithOptions method grabs the pre-built tabBar, setting its delegate to self (e.g. the AppDelegate class).

-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:
// ----------------------------------------------------------
// Set the TabBarController delegate to be 'self'
// ----------------------------------------------------------
_tabBarController = (UITabBarController*)self.window.rootViewController;

//  tabController.selectedIndex = [defaults integerForKey:kOptionLastTabSelectedKey];
_tabBarController.delegate = self;

// update tab bar per iAdSuite approach
[self updateiAd];

I then built a new set of controllers per the iAdSuite approach and reset the tab bar with these new tab bar items.

-(void)updateiAd {
NSArray* viewControllers = [_tabBarController viewControllers];
NSMutableArray*newViewControllers = [[NSMutableArray alloc] init];
BannerViewController*bvc=NULL;
for(UIViewController * vc in viewControllers) {
    bvc = [[BannerViewController alloc] initWithContentViewController:vc];
    [newViewControllers addObject:bvc];
}

// set the new view controllers, replacing the original set
[_tabBarController setViewControllers:newViewControllers];
}

This approach puts the same 'ad' at the bottom of each view, exactly as needed. I also had to set the view title in the viewDidLoad method of each custom viewController (somehow, setting it on the bar item didn't seem to work not did setting the image; the later may reflect an issue with my images however).

My original configuration was

                           TabViewController

NavController1               NavController2          NavController3  ...
      |                            |                       |
CustomViewController1      CustomViewController2      CustomViewController3

My final configuration is now

                           TabViewController

NavController1               NavController2          NavController3  ...
     |                            |                       |
iAdView1                         iAdView2               iAdView3
    |                            |                       |
CustomViewController1      CustomViewController2      CustomViewController3

In terms of view lifecycle, I should add that only the NavControllers are in existence at the time the updateiAd method is called.

The individual CustomViewControllers1/2/3/etc get created after the call completes.

chris fabri
  • 1
  • 1
  • 1