I am integrating iAds into an iOS 6 app with a UITabBarController and UINavigationController and a bunch of UITableView-based screens and have it mostly working with one glaring exception. When I navigate from a parent to child view controller, my banner view does not appear until the ADBannerView delegate methods are invoked (when a new ad is served) even if I already have an ad.
What I want is when I have an ad displayed on the parent and click to the child, I want the ad to appear immediately. However, what is happening is that the ad banner disappears and does reappear until the delegate methods are invoked to update it. Stepping through the debugger it all looks like it should work. I am sure it is something trivial I am not doing, but am stuck. Help!
As I put in the comments, it seems to be that the _contentView frame size is incorrect at the point where the code is placing the ad in the view. With auto-layout off, it places the ad correct initially, but with each new ad, the banner moves up the screen. With auto-layout on, the initial display of the ad is in the wrong place, but subsequent calls (or rotation calls) the ad is in the correct location.
Here is the relevant code:
I have a singleton class that manages the creation of the ADBannerView instance I want to share. The singleton just does the allocation once; otherwise it returns the banner view:
- (ADBannerView *) getAdBannerSingleton
{
if ( bannerView == nil )
{
DDLogInfo(#"iAds: Creating our initial banner view") ;
bannerView = [[ADBannerView alloc] initWithAdType:ADAdTypeBanner] ;
// to handle rotation properly
[bannerView setAutoresizingMask:UIViewAutoresizingFlexibleWidth] ;
}
return bannerView ;
}
I have a base class that has a View --> SubView --> UITableView (in a XIB) and manages the ADBannerView and serves as the delegate. My concrete parent/child classes inherit from the base class, but do nothing special themselves (they are blissfully unaware of the ads).
#implementation SSSAdEnabledTableViewController
#synthesize tableView = _tableView;
#synthesize contentView = _contentView;
#synthesize bannerView = _bannerView ;
- (id) initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil] ;
// this class does not need to do anything special as there are no concrete instances of it
return self ;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
//
// manage our ad
//
DDLogInfo(#"iAds: Managing ad View from viewWillAppear for %#",
NSStringFromClass([self class])) ;
[self manageAdView] ;
}
- (void) viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated] ;
// no effect with our without the call below
[_bannerView removeFromSuperview];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[_tableView setAutoresizesSubviews:YES] ;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
self.contentView = nil ;
self.tableView = nil ;
self.bannerView = nil ;
}
#pragma mark -- iAd framework methods
- (void) bannerViewDidLoadAd:(ADBannerView *)banner
{
DDLogInfo(#"iAds: Banner ad successfully loaded for %#", NSStringFromClass([self class])) ;
[self manageAdView] ;
}
- (BOOL)bannerViewActionShouldBegin:(ADBannerView *)banner willLeaveApplication:(BOOL)willLeave
{
return YES ;
}
- (void)bannerViewActionDidFinish:(ADBannerView *)banner
{
// don't think we have to do anything?
}
- (void) bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
DDLogError(#"Error loading iAd for %#. Reason: %#",
NSStringFromClass([self class]),
[error localizedDescription]) ;
[self manageAdView] ;
}
#pragma mark -- ad banner helper methods
//
// Get our shared banner view instance
//
- (void)createAdBannerView
{
// get the banner view instance
_bannerView = [[SSSStoreManager sharedStore] getAdBannerSingleton] ;
// make us the delegate
DDLogInfo(#"iAds: Setting %# to be our banner delegate", NSStringFromClass([self class])) ;
// set it to be the delegate
[_bannerView setDelegate:self] ;
}
//
// Based on the ad status, make room for it in our view or hide it
//
- (void)manageAdView
{
// make sure we always have a banner view to work with here
if ( _bannerView == nil )
{
DDLogInfo(#"Banner View is Nil; Creating it for %#", NSStringFromClass([self class]));
[self createAdBannerView] ;
}
// will hold our banner dimensions and position
CGRect bannerFrame = CGRectZero;
// our inner view dimensions and position
CGRect contentViewFrame = _contentView.frame;
// make sure all of the changes happen at one time
[UIView beginAnimations:#"fixupViews" context:nil];
// is the banner loaded and should it be visible?
if ( _bannerView.bannerLoaded == YES )
{
DDLogInfo(#"iAds: Showing banner view for %#", NSStringFromClass([self class])) ;
bannerFrame.size = [_bannerView sizeThatFits:contentViewFrame.size];
contentViewFrame.size.height -= bannerFrame.size.height;
bannerFrame.origin.y = contentViewFrame.size.height;
}
else
{
DDLogInfo(#"iAds: Hiding banner view for %#", NSStringFromClass([self class])) ;
bannerFrame.origin.y = contentViewFrame.size.height;
}
// reset our content view based on whether we have an ad to show
_contentView.frame = contentViewFrame ;
[self.view addSubview:_bannerView];
_bannerView.frame = bannerFrame;
[UIView commitAnimations];
}
Related
I've built a custom UITabBarController with Storyboards/Segues and UIViewController containment. Here is a link to it: https://github.com/mhaddl/MHCustomTabBarController
The UIViewControllers which will be presented by the Container are stored in a NSMutableDictionary (keys are the segues' identifiers). Everything is working fine until the point is reached where I come back to a earlier presented ViewController. At this moment "dealloc" gets called on this ViewController before it is presented.
How can I prevent "dealloc" from getting called so it can be used to unsubscribe from Notifications, and nil delegates.
MHCustomTabBarController:
#implementation MHCustomTabBarController {
NSMutableDictionary *_viewControllersByIdentifier;
}
- (void)viewDidLoad {
[super viewDidLoad];
_viewControllersByIdentifier = [NSMutableDictionary dictionary];
}
-(void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if (self.childViewControllers.count < 1) {
[self performSegueWithIdentifier:#"viewController1" sender:[self.buttons objectAtIndex:0]];
}
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
self.destinationViewController.view.frame = self.container.bounds;
}
#pragma mark - Segue
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if (![segue isKindOfClass:[MHTabBarSegue class]]) {
[super prepareForSegue:segue sender:sender];
return;
}
self.oldViewController = self.destinationViewController;
//if view controller isn't already contained in the viewControllers-Dictionary
if (![_viewControllersByIdentifier objectForKey:segue.identifier]) {
[_viewControllersByIdentifier setObject:segue.destinationViewController forKey:segue.identifier];
}
for (UIButton *aButton in self.buttons) {
[aButton setSelected:NO];
}
UIButton *button = (UIButton *)sender;
[button setSelected:YES];
self.destinationIdentifier = segue.identifier;
self.destinationViewController = [_viewControllersByIdentifier objectForKey:self.destinationIdentifier];
}
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
if ([self.destinationIdentifier isEqual:identifier]) {
//Dont perform segue, if visible ViewController is already the destination ViewController
return NO;
}
return YES;
}
#pragma mark - Memory Warning
- (void)didReceiveMemoryWarning {
[[_viewControllersByIdentifier allKeys] enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop) {
if (![self.destinationIdentifier isEqualToString:key]) {
[_viewControllersByIdentifier removeObjectForKey:key];
}
}];
}
#end
MHTabBarSegue:
#implementation MHTabBarSegue
- (void) perform {
MHCustomTabBarController *tabBarViewController = (MHCustomTabBarController *)self.sourceViewController;
UIViewController *destinationViewController = (UIViewController *) tabBarViewController.destinationViewController;
//remove old viewController
if (tabBarViewController.oldViewController) {
[tabBarViewController.oldViewController willMoveToParentViewController:nil];
[tabBarViewController.oldViewController.view removeFromSuperview];
[tabBarViewController.oldViewController removeFromParentViewController];
}
destinationViewController.view.frame = tabBarViewController.container.bounds;
[tabBarViewController addChildViewController:destinationViewController];
[tabBarViewController.container addSubview:destinationViewController.view];
[destinationViewController didMoveToParentViewController:tabBarViewController];
}
#end
"At this moment "dealloc" gets called on this ViewController before it is presented." -- no, not really. Dealloc is being called on a controller that never gets on screen, not the one you came from initially or are going back to. The way your segue is set up, and the fact that you keep a reference to your controllers in the dictionary, means that they never get deallocated. Segues (other than unwinds) ALWAYS instantiate new view controllers, so what's happening is that a new instance of, say VC1 is created when you click on the first tab (and a segue is triggered), but you never do anything with that controller (which would be self.destinationViewController in the custom segue class) so it's deallocated as soon as the perform method exits.
Depending on where you setup any delegates or notification observers, this might not be a problem -- this controller that's created, and then immediately deallocated never has its viewDidLoad method called, so if you do those things in viewDidLoad, they won't ever happen for this transient view controller.
If you don't want this to happen, then you need to make your transitions in code without using segues.
i would add ADMOB to my xcode project, but when a test it on iphone and simulator i receive this error :
AdMob Ios Error: Failed to receive ad with error: Request Error: No ad to show.
my code Banner.h:
#import <UIKit/UIKit.h>
#import "GADBannerViewDelegate.h"
#class GADBannerView, GADRequest;
#interface BannerExampleViewController : UIViewController
<GADBannerViewDelegate> {
GADBannerView *adBanner_;
}
#property (nonatomic, retain) GADBannerView *adBanner;
- (GADRequest *)createRequest;
#end
Banner.m
#import "BannerExampleViewController.h"
#import "GADBannerView.h"
#import "GADRequest.h"
#import "SampleConstants.h"
#implementation BannerExampleViewController
#synthesize adBanner = adBanner_;
#pragma mark init/dealloc
// Implement viewDidLoad to do additional setup after loading the view,
// typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
// Initialize the banner at the bottom of the screen.
CGPoint origin = CGPointMake(0.0,
self.view.frame.size.height -
CGSizeFromGADAdSize(kGADAdSizeBanner).height);
// Use predefined GADAdSize constants to define the GADBannerView.
self.adBanner = [[[GADBannerView alloc] initWithAdSize:kGADAdSizeBanner
origin:origin]
autorelease];
// Note: Edit SampleConstants.h to provide a definition for kSampleAdUnitID
// before compiling.
self.adBanner.adUnitID = kSampleAdUnitID;
self.adBanner.delegate = self;
[self.adBanner setRootViewController:self];
[self.view addSubview:self.adBanner];
self.adBanner.center =
CGPointMake(self.view.center.x, self.adBanner.center.y);
[self.adBanner loadRequest:[self createRequest]];
}
- (void)dealloc {
adBanner_.delegate = nil;
[adBanner_ release];
[super dealloc];
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
#pragma mark GADRequest generation
// Here we're creating a simple GADRequest and whitelisting the application
// for test ads. You should request test ads during development to avoid
// generating invalid impressions and clicks.
- (GADRequest *)createRequest {
GADRequest *request = [GADRequest request];
// Make the request for a test ad. Put in an identifier for the simulator as
// well as any devices you want to receive test ads.
request.testDevices =
[NSArray arrayWithObjects:#"6a47e320f03fe2ab9854afe2e5708321", nil];
return request;
}
#pragma mark GADBannerViewDelegate impl
// We've received an ad successfully.
- (void)adViewDidReceiveAd:(GADBannerView *)adView {
NSLog(#"Received ad successfully");
}
- (void)adView:(GADBannerView *)view
didFailToReceiveAdWithError:(GADRequestError *)error {
NSLog(#"Failed to receive ad with error: %#", [error localizedFailureReason]);
}
#end
One more thing to point out - if your banner space width/height is set ot 0, you'll receive exactly the same error. I'm currently trying to make a workaround about this because I want to animate ad view on screen only if it successfully load ad.
This is because the server have not update yet.
You should wait for 15 minutes and try again, it will work correctly.
PS: Remember replace kSampleAdUnitID with your App ID in Admob
This code work correctly when I am testing with my ID
I am using Google AdMobs DFP to serve up mediation banners from other networks. I have added Millennial and InMobi fine but now I need to add a network (YOC group) that does not have an adapter for DFP so I need to implement 'custom event banners'. I have read the guides and technical documents on implementing unsupported mediator networks with custom events but still cannot understand how it hooks up the two SDKs (Google AdMobs SDK and the mediator network's SDK).
The network that doesn't have an adapter (YOC) works if I hard-code the yoc ad id (of the format '9A9A9AA99999999A999AA9A99AA99AAAA999A9AA') to a sample ad and send off the request. The banner comes back fine and uses the YOC SDK to show an interactive/rich media advert.
However in my app I only have a Google DFP account id (of the format '/9999/company.app/channel') which I send off a request for using Google AdMobs SDK to DFP. The request then returns a response with the specific mediator ad network to try and request a banner advert from. My current setup is that YOC serves 100% of ads in DFP.
Problem: I get a banner advert returned from the YOC ad network and it displays on screen. It registers page impressions (with DFP) but there is no response to a touch/press event the way it works as if I hard-code the initialisation parameters of the yoc ad view. I can't however hard-code the yoc ad id (when initialising) because it would only work for one banner advert and I need different banners for each specific advert in each channel.
Below is the sample code I am trying to implement using just NSLogs in the methods to log to the console and show that the methods are being called. It is a very basic app and puts all the code in one place for ease of reading.
AppDelegate.h
#import < UIKit/UIKit.h>
#import "GADBannerView.h"
#import "GADBannerViewDelegate.h"
#import "GADCustomEventBanner.h"
#import "GADCustomEventBannerDelegate.h"
#import < YOCAdSDK/YOCAdView.h>
#interface AppDelegate : UIResponder <UIApplicationDelegate, GADBannerViewDelegate, GADCustomEventBanner, GADCustomEventBannerDelegate, YOCAdViewDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) UIViewController *root;
#end
AppDelegate.m
#import "AppDelegate.h"
#import "GADBannerView.h"
#import <YOCAdSDK/YOCAdSize.h>
#implementation AppDelegate
#synthesize root;
#synthesize delegate; // GADCustomEventBannerDelegate set on GADCustomEventBanner
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
CGRect bounds = [[UIScreen mainScreen] bounds];
self.window = [[UIWindow alloc] initWithFrame:bounds];
self.window.backgroundColor = [UIColor greenColor];
GADBannerView *banner = [[GADBannerView alloc] initWithAdSize:kGADAdSizeLeaderboard origin:CGPointMake(0, 0)];
self.root = [[UIViewController alloc] initWithNibName:nil bundle:nil];
UIView *base = [[UIView alloc] initWithFrame:bounds];
base.backgroundColor = [UIColor greenColor];
self.root.view = base;
// the adUnitID is always of our DFP account number of the format '/9999/company.app/aa_aa<channelName>_<channelName>app'
banner.adUnitID = #"/9999/company.app/channel_specific_id";
banner.delegate = self;
banner.rootViewController = self.root;
self.delegate = self;
[base addSubview:banner];
[base bringSubviewToFront:banner];
[banner loadRequest:[GADRequest request]];
[self.window setRootViewController:self.root];
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone];
[self.window makeKeyAndVisible];
return YES;
}
#pragma mark GADBannerViewDelegate
- (void)adViewDidReceiveAd:(GADBannerView *)view {
NSLog(#" adViewDidReceiveAd ");
NSLog(#" view: %# ", [view description]);
// for other ad networks here we get view.mediatedAdView = IMAdView (InMobi) or view.mediatedAdView = MMAdView (Millennial) but with YOC view.mediatedAdView = nil;
[self.delegate customEventBanner:self didReceiveAd:view];
}
- (void)adView:(GADBannerView *)view didFailToReceiveAdWithError:(GADRequestError *)error {
NSLog(#" didFailToReceiveAdWithError ");
[self.delegate customEventBanner:self didFailAd:error];
}
- (void)adViewWillPresentScreen:(GADBannerView *)adView {
NSLog(#" adViewWillPresentScreen ");
[self.delegate customEventBanner:self clickDidOccurInAd:adView];
[self.delegate customEventBannerWillPresentModal:self];
}
- (void)adViewWillDismissScreen:(GADBannerView *)adView {
NSLog(#" adViewWillDismissScreen ");
[self.delegate customEventBannerWillDismissModal:self];
}
- (void)adViewDidDismissScreen:(GADBannerView *)adView {
NSLog(#" adViewDidDismissScreen ");
[self.delegate customEventBannerDidDismissModal:self];
}
- (void)adViewWillLeaveApplication:(GADBannerView *)adView {
NSLog(#" adViewWillLeaveApplication ");
[self.delegate customEventBannerWillLeaveApplication:self];
}
#pragma mark GADCustomEventBanner
- (void)requestBannerAd:(GADAdSize)adSize
parameter:(NSString *)serverParameter
label:(NSString *)serverLabel
request:(GADCustomEventRequest *)request {
NSLog(#" requestBannerAd ");
// not sure if we initialiase the YOC tag here or how we would do this if can't hard code the yocTag to the format '9A9A9AA99999999A999AA9A99AA99AAAA999A9AA'
// and we only have the banner view returned from DFP with the id '/9999/company.app/channel_specific_id)'
YOCAdView *yocAdView = [[YOCAdView alloc] initWithYOCTag:serverParameter delegate:self size:kLeaderboard728x90 position:CGPointMake(0, 0)];
yocAdView.delegate = self;
[yocAdView load];
[self.root.view addSubview:yocAdView];
}
#pragma mark GADCustomEventBannerDelegate
- (void)customEventBanner:(id<GADCustomEventBanner>)customEvent didReceiveAd:(UIView *)view {
NSLog(#" [(GADBannerView *)view adUnitID]: %# ", [(GADBannerView *)view adUnitID]);
NSLog(#" [(GADBannerView *)view delegate]: %# ", [(GADBannerView *)view delegate]);
NSLog(#" [(id<YOCAdViewDelegate>)[customEvent delegate] viewControllerForPresentingYOCAdView]: %# ", [(id<YOCAdViewDelegate>)[customEvent delegate] viewControllerForPresentingYOCAdView]);
// not sure if we initialiase the YOC tag here or how we would do this if can't hard code the yocTag to '9A9A9AA99999999A999AA9A99AA99AAAA999A9AA"
// and we only have the banner view returned from DFP with the id '/9999/company.app/channel_specific_id'
[customEvent requestBannerAd:kGADAdSizeLeaderboard parameter:#"???" label:nil request:nil];
// the key might be that the [customEvent delegate] is of type YOCAdViewDelegate and we can do code specific to YOC here...
// but again not sure how to initialize the YOCAdView because we already have the banner view returned from DFP (with a different format account id)
// [(id<YOCAdViewDelegate>)[customEvent delegate] yocAdViewDidInitialize:yocAdView];
}
- (void)customEventBanner:(id<GADCustomEventBanner>)customEvent didFailAd:(NSError *)error {
NSLog(#" customEventBanner:didFailAd ");
}
- (void)customEventBanner:(id<GADCustomEventBanner>)customEvent clickDidOccurInAd:(UIView *)view {
NSLog(#" customEventBanner:clickDidOccurInAd ");
}
- (void)customEventBannerWillPresentModal:(id<GADCustomEventBanner>)customEvent {
NSLog(#" customEventBannerWillPresentModal ");
}
- (void)customEventBannerWillDismissModal:(id<GADCustomEventBanner>)customEvent {
NSLog(#" customEventBannerWillDismissModal ");
}
- (void)customEventBannerDidDismissModal:(id<GADCustomEventBanner>)customEvent {
NSLog(#" customEventBannerDidDismissModal ");
}
- (void)customEventBannerWillLeaveApplication:(id<GADCustomEventBanner>)customEvent {
NSLog(#" customEventBannerWillLeaveApplication ");
}
#pragma mark YOCAdViewDelegate
- (UIViewController *)viewControllerForPresentingYOCAdView {
NSLog(#" viewControllerForPresentingYOCAdView ");
return self.root;
}
- (void)yocAdViewDidInitialize:(YOCAdView *)yocAdView {
NSLog(#" yocAdViewDidInitialize ");
}
- (void)yocAdView:(YOCAdView *)yocAdView didFailWithError:(NSError *)error {
NSLog(#" didFailWithError: %# ", error);
}
- (void)yocAdViewDidHide:(YOCAdView *)yocAdView {
NSLog(#" yocAdViewDidHide ");
}
- (void)yocAdViewDidReload:(YOCAdView *)yocAdView {
NSLog(#" yocAdViewDidReload ");
}
- (void)yocAdViewWillPresentModalViewController:(YOCAdView *)yocAdView {
NSLog(#" yocAdViewWillPresentModalViewController ");
}
- (void)yocAdViewWillDismissModalViewController:(YOCAdView *)yocAdView {
NSLog(#" yocAdViewWillDismissModalViewController ");
}
#end
Please can someone help give me things to try out and find out how to get the advert banner view returned from Google DFP responding to click events!?
There is a guide here on how to develop custom events.
If you're using AdMob SDK Mediation with ad unit /9999/company.app/channel, then this ad unit should be an SDK Mediation creative within DFP. One of your networks should be a Custom Event with the following settings:
Parameter: 9A9A9AA99999999A999AA9A99AA99AAAA999A9AA (or whatever your YOC tag is)
Label: YOC Group (This is just a label so you remember what this custom event is for)
Class Name: AppDelegate (You should really implement the custom event in it's own
class, and replace the class name with that class)
Then when implementing the custom event, you can reference serverParameter as the YOCTag like you already have.
You don't want to implement GADCustomEventBannerDelegate yourself. By your class implementing GADCustomEventBanner, and adding:
#synthesize delegate
at the top of your implementation, you have access to an instance of this object via:
self.delegate
You'll want to use that delegate to tell the custom event (and thereby AdMob Mediation) that YOC returned or failed to return an ad. Based on the YOC delegate, your mapping might look something like this:
- (UIViewController *)viewControllerForPresentingYOCAdView {
NSLog(#" viewControllerForPresentingYOCAdView ");
return self.root;
}
- (void)yocAdViewDidInitialize:(YOCAdView *)yocAdView {
NSLog(#" yocAdViewDidInitialize ");
// Assuming this means the yocAdView was received.
[self.delegate customEventBanner:self didReceiveAd:yocAdView];
}
- (void)yocAdView:(YOCAdView *)yocAdView didFailWithError:(NSError *)error {
NSLog(#" didFailWithError: %# ", error);
[self.delegate customEventBanner:self didFailAd:error];
}
- (void)yocAdViewDidHide:(YOCAdView *)yocAdView {
NSLog(#" yocAdViewDidHide ");
}
- (void)yocAdViewDidReload:(YOCAdView *)yocAdView {
NSLog(#" yocAdViewDidReload ");
[self.delegate customEventBanner:self didReceiveAd:yocAdView];
}
- (void)yocAdViewWillPresentModalViewController:(YOCAdView *)yocAdView {
NSLog(#" yocAdViewWillPresentModalViewController ");
[self.delegate customEventBanner:self clickDidOccurInAd:yocAdView];
[self.delegate customEventBannerWillPresentModal:self];
[self.delegate customEventBannerWillLeaveApplication:self];
}
- (void)yocAdViewWillDismissModalViewController:(YOCAdView *)yocAdView {
NSLog(#" yocAdViewWillDismissModalViewController ");
[self.delegate customEventBannerWillDismissModal:self];
}
Finally, you don't want to invoke GADCustomEventBannerDelegate methods in your GADBannerViewDelegate callback methods. These are invoked by AdMob telling you that mediation came back with an ad. The GADBannerViewDelegate implementation is part of your main app, and should stay out of your custom event class's implementation.
I know the guide invokes the custom event delegate methods in its GADBannerViewDelegate implementation. The difference is the guide is writing a custom event to implement AdMob, so in the context of the guide, the GADBannerViewDelegate is behaving like the YOCAdViewDelegate in your example.
I have a Test application setup with AdMob Mediation service being used, only on testing device at the moment. I have setup all the required methods per the documentation. I am having an issue where when the Fail to Receive Ad error occurs, no more ads are requested or shown?
Header:
#import <UIKit/UIKit.h>
#import "GADBannerViewDelegate.h"
#class GADBannerView, GADRequest;
#interface AdTestViewController : UIViewController
<GADBannerViewDelegate> {
GADBannerView *bannerView_;
}
#property (nonatomic, retain) GADBannerView *bannerView;
- (GADRequest *)createRequest;
#end
Imp File
#import "AdTestViewController.h"
#import "Constants.h"
#import "GADBannerView.h"
#import "GADRequest.h"
#implementation AdTestViewController
#synthesize bannerView = bannerView_;
- (void)viewDidLoad {
[super viewDidLoad];
// Create a view of the standard size at the top of the screen.
// Available AdSize constants are explained in GADAdSize.h.
//bannerView_ = [[GADBannerView alloc] initWithAdSize:kGADAdSizeBanner];
// Initialize the banner at the bottom of the screen.
//CGPoint origin = CGPointMake(0.0,
// self.view.frame.size.height -
// CGSizeFromGADAdSize(kGADAdSizeBanner).height);
self.bannerView = [[GADBannerView alloc] initWithAdSize:kGADAdSizeBanner];
//origin:origin];
self.bannerView.adUnitID = kAdMobPublisherID;
self.bannerView.delegate = self;
[self.bannerView setRootViewController:self];
[self.view addSubview:self.bannerView];
self.bannerView.center =
CGPointMake(self.view.center.x, self.bannerView.center.y);
[bannerView_ loadRequest:[self createRequest]];
bannerView_.backgroundColor = [UIColor blueColor];
// Make the request for a test ad. Put in an identifier for
// the simulator as well as any devices you want to receive test ads.
GADRequest *request = [GADRequest request];
request.testDevices = [NSArray arrayWithObjects:
#"4D047EB9-A3A7-441E-989E-C5437F05DB04",
#"YOUR_DEVICE_IDENTIFIER",
nil];
}
- (GADRequest *)createRequest {
GADRequest *request = [GADRequest request];
// Make the request for a test ad. Put in an identifier for the simulator as
// well as any devices you want to receive test ads.
request.testDevices = [NSArray arrayWithObjects:
#"4D047EB9-A3A7-441E-989E-C5437F05DB04",
#"YOUR_DEVICE_IDENTIFIER",
nil];
return request;
}
- (void)adView:(GADBannerView *)view didFailToReceiveAdWithError:(GADRequestError *)error;
{
NSLog(#"Error - did Fail to Receive an Ad");
bannerView_.hidden = YES;
}
- (void)adViewDidReceiveAd:(GADBannerView *)view;
{
NSLog(#"Ad Received");
bannerView_.hidden = NO;
}
#end
What I am seeing in my logs is the 'Ad Received' a few times, then 'Error - did Fail to Receive an Ad'... After this log there are no further entries it is like it stops requesting? Testing only on simulator at present.
Any ideas how to solve this, or potentially an alternative method on hiding the view when an error/no ad is received?
I find the same thing – when the GADBannerView is hidden, no more requests are sent out.
One thing I tried successfully is to move the GADBannerView offscreen instead of hiding it. Of course, you only want to do this as a consequence of didFailToReceiveAdWithError, and then move it back onscreen when adViewDidReceiveAd. I got this working so the user sees a nice animation when ads come and go, much like iAd encourages.
In short, the code below will place your GADBannerView (here called mAdBannerView) either at the bottom of the screen or offscreen, depending on the boolean adIsLoaded.
CGRect bannerFrame = mAdBannerView.frame;
bannerFrame.origin.y = self.view.bounds.size.height - (adIsLoaded * bannerFrame.size.height);
mAdBannerView.frame = bannerFrame;
in the method that's called when there is an error put in some thing like
bannerView_.hidden = 1;
that will hide the view if there's an error and it will probably automatically be displayed if an ad was received with no error
Think you're better off just hiding the bannerView_ with the hidden property.
- (void)adView:(GADBannerView *)view didFailToReceiveAdWithError:(GADRequestError *)error {
bannerView_.hidden = YES;
}
Of course you have to remember to set hidden back to YES when an ad is successfully received.
Simple solution, set the bannerView_.hidden true in adView:didFailToReciewvwAdWithError method. And to retrieve the view use adViewDidReceiveAd method. Example code:
These are ADmob's delegate method:
- (void)adView:(GADBannerView *)view didFailToReceiveAdWithError:(GADRequestError *)error
{
bannerView_.hidden = YES;
}
- (void)adViewDidReceiveAd:(GADBannerView *)view
{
bannerView_.hidden = NO;
}
I had the same problem, this worked for me:
Do not use the .hidden property to hide AdMob ads.
Just set the alpha to 0 (invisible) or 1 (visible).
So in your GADBannerView delegate method...
-(void)adView:(GADBannerView *)bannerView didFailToReceiveAdWithError:(GADRequestError *)error {
// Hide the ad banner.
[UIView animateWithDuration:0.5 animations:^{
self.myADBanner.alpha = 0.0;
}];
}
-(void)adViewDidReceiveAd:(GADBannerView *)bannerView {
//Show the ad banner.
[UIView animateWithDuration:0.5 animations:^{
self.myADBanner.alpha = 1.0;
}];
}
With regards to "After this log there are no further entries it is like it stops requesting?"
This happens to me as well when I remove an ad from the view hierarchy. However, requests continue when I add the ad back to the view hierarchy. The only time they didn't continue was when I was using the .hidden property.
When my application launches, iAd refuses to load an ad on the first screen. No error message, nothing. If I switch screens (going to another screen), it starts getting ads and serving them up, even when I go back to the first screen.
I am using a single iAd instance that is in the ApplicationDelegate. I am attempting to link in the iAdBanner in viewDidAppear, and unlink in viewWillDisappear.
The viewDidAppear method:
- (void)viewDidAppear:(BOOL)animated
{
NSLog(#"view did appear");
[super viewDidAppear:animated];
ADBannerView *adBanner = SharedAdBannerView;
adBanner.requiredContentSizeIdentifiers = (&ADBannerContentSizeIdentifierPortrait != nil) ?
[NSSet setWithObjects:ADBannerContentSizeIdentifierPortrait, ADBannerContentSizeIdentifierLandscape, nil] :
[NSSet setWithObjects:ADBannerContentSizeIdentifier320x50, ADBannerContentSizeIdentifier480x32, nil];
[self.view addSubview:adBanner];
// set the delegate to self, so that we are notified of ad responses
[adBanner setDelegate:self];
isAdShown = [adBanner isBannerLoaded];
[self layoutForCurrentOrientation:animated];
}
The layout method:
- (void)layoutForCurrentOrientation:(BOOL)animated
{
//TODO: this only handles bottom-located elements
ADBannerView *adBanner = SharedAdBannerView;
CGFloat animationDuration = animated ? 0.2f : 0.0f;
// by default content consumes the entire view area
CGRect contentFrame = contentView.bounds;
CGRect owningViewFrame = [self view].bounds;
// the banner still needs to be adjusted further, but this is a reasonable starting point
// the y value will need to be adjusted by the banner height to get the final position
CGPoint bannerOrigin = CGPointMake(CGRectGetMinX(owningViewFrame), CGRectGetMaxY(owningViewFrame));
CGFloat bannerHeight = 0.0f;
// First, setup the banner's content size and adjustment based on the current orientation
if(UIInterfaceOrientationIsLandscape(self.interfaceOrientation))
adBanner.currentContentSizeIdentifier = (&ADBannerContentSizeIdentifierLandscape != nil) ? ADBannerContentSizeIdentifierLandscape : ADBannerContentSizeIdentifier480x32;
else
adBanner.currentContentSizeIdentifier = (&ADBannerContentSizeIdentifierPortrait != nil) ? ADBannerContentSizeIdentifierPortrait : ADBannerContentSizeIdentifier320x50;
bannerHeight = adBanner.bounds.size.height;
// Depending on if the banner has been loaded, we adjust the content frame and banner location
// to accomodate the ad being on or off screen.
// This layout is for an ad at the bottom of the view.
if (isAdShown)
{
NSLog(#"Banner is loaded");
contentFrame.size.height = owningViewFrame.size.height - bannerHeight;
bannerOrigin.y -= bannerHeight;
}
else
{
NSLog(#"Banner is not loaded");
bannerOrigin.y += bannerHeight;
contentFrame.size.height = owningViewFrame.size.height;
}
NSLog(#"Banner content Frame: (%f, %f), (%f, %f)", bannerOrigin.x, bannerOrigin.y, contentFrame.size.width, contentFrame.size.height);
// And finally animate the changes, running layout for the content view if required.
[UIView animateWithDuration:animationDuration
animations:^{
contentView.frame = contentFrame;
[contentView layoutIfNeeded];
adBanner.frame = CGRectMake(bannerOrigin.x, bannerOrigin.y, adBanner.frame.size.width, adBanner.frame.size.height);
}];
}
and the viewWillDisappear method:
-(void)viewWillDisappear:(BOOL)animated
{
NSLog(#"View will disappear");
[super viewWillDisappear:animated];
[self removeLinkToAdBanner:animated];
}
-(void)removeLinkToAdBanner:(BOOL)animated
{
ADBannerView *adBanner = SharedAdBannerView;
if ([adBanner delegate] == self) {
adBanner.delegate = nil;
[adBanner removeFromSuperview];
}
}
The real frustration was this was working in the simulator before I upgraded to xcode 4. I upgraded, and all of a sudden it has stopped working. Anyone else seen behavior like this? Any ideas what I can do to fix it? The behavior occurs in the simulator on all test versions of 4.x (4.0 through 4.3).
I have encountered the same problem, and the fix is in the AppDelegate's bannerViewDidLoadAd method. You need to call your showBanner method there in order for the ad to display initially.
- (void)bannerViewDidLoadAd:(ADBannerView *)banner
{
[_currentController showBannerView:_bannerView animated:YES];
}
Please check the new iAdSuite sample codes where I took this out.
iAdSuite Sample Code Updated 10/31/11