Im having a little problem with ADBanner's, I have 3 UIViewController's and each one have an ADBanner. I have set up the delegate methods like this:
-(void)bannerViewActionDidFinish:(ADBannerView *)banner {
[UIView beginAnimations:#"animateAdBannerOn" context:nil];
banner.frame = CGRectOffset(banner.frame, 0, 320);
[UIView commitAnimations];
}
-(void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error {
NSLog(#"Error: %#", error);
[UIView beginAnimations:#"animateAdBannerOff" context:nil];
banner.frame = CGRectOffset(banner.frame, 0, 320+banner.frame.size.height);
[UIView commitAnimations];
}
In every UIViewController class file, Im using UIStoryboard. The delegate is connected properly.
Now the issue.
In the initial ViewController the ADBanner loads like it should, but when I click it it the ad loads in fullscreen, but when I click the x button to get out of the ad I get an error from the didFailToReceiveAdWithError saying: NSLocalizedFailureReason=Loading throttled and the ADBanner disappears. This error happens for every ADBanner. There is 1 more strange thing happening. If I dont click the ad in the initial ViewController the ADBanners in the other 2 ViewControllers the ad doesnt load and gives me the same error from above but 2 times. If i click and dissmisses the ADBanner in the initial ViewController the other 2 loads without an error, but the one in the initial does.
To make it short, when the initial ViewControllers ADBanner loads the other 2 dont, and when the other 2 loads the initial doesnt
Why is this happening I find this very weird. I read the documentations but I didnt find anything if there was a limit of ADBanners you can have.
According to this answer Apple sends this error on purpose. They do that to make sure you can handle the error properly. Also you should not be creating separate iAds but using 1 in all view controllers. Please read this article on how to create shared iAds.
Related
#interface ViewController
#property(weak) IBOutlet UIView *blackView;
#end
(1) First I tried to use animation block
- (IBAction) buttonHitted:(id)sender
{
[UIView beginAnimations: nil context: NULL];
[UIView setAnimationBeginsFromCurrentStatus: NO];
self.blackView.center = CGPointMake(UIScreen.mainScreen.bounds.size.width - self.blackView.center.x, self.blackView.center.y);
[UIView commitAnimations];
}
And the Animations ALWAYS begins from current status, and I can't find out why for long.
(2)Then I tried to use code block, it is the sad same.
- (IBAction) buttonHitted:(id)sender
{
[UIView animateWithDuration:2.0 delay:0.0 options:0
animations:^(void)
{
self.blackView.center = CGPointMake(UIScreen.mainScreen.bounds.size.width - self.blackView.center.x, self.blackView.center.y);
}
completion:nil];
}
//Options to 0, indicating that UIViewAnimationOptionBeginFromCurrentState not setted
I just wanna know why the animation ALWAYS begins from current status. I checked the property of the view, it is always the last value of the current animation.
I think I found out the reason behind it.
Animations for property(like UIView_ins.center and CALayer_ins.transform) could be mixed together, even you just changed one part of it, like just center.x
What's more, CAAnimation must set this property to YES, so the default CAAction for the transform must set this to YES too.
#property(getter=isAdditive) BOOL additive;
SO the new animation do not override the flight-in one, but just mixed them together.
To make sure it change UIView animation options to UIViewAnimationOptionCurveLinear
- (IBAction) buttonHitted:(id)sender
{
[UIView animateWithDuration:3.0 delay:0.0
options:UIViewAnimationOptionCurveLinear
animations:^(void)
{
self.blackView.center = CGPointMake(UIScreen.mainScreen.bounds.size.width - self.blackView.center.x, self.blackView.center.y);
}
completion:nil];
}
You will see the lovely UIView standing there for a long time and did nothing, that is the symbol of mixed in linear way :)
While using the default EaseInOut options, it looks like the problem of setAnimationBeginsFromCurrentStatus:.
At https://developer.apple.com/library/ios/technotes/tn2286/_index.html, Apple recommends that applications only share an instance of an ADBannerView. Now I have two UIViewControllers so I have the following code set up:
Singleton.h
ADBannerView *iadBanner;
Singleton.m
- (void)bannerViewDidLoadAd:(ADBannerView *)banner {
NSLog(#"Bank loaded iAd!");
[ViewController1 showiAd:(iadBanner)];
[ViewController2 showiAd:(iadBanner)];
}
ViewController1.h
ADBannerView *iadBanner;
ViewController1.m
- (void)showiAd:(ADBannerView *)ad {
NSLog(#"VC iAd Shown!");
iadBanner = ad;
[iadBanner setFrame:CGRectMake(0, 20, 320, 50)];
[self.view addSubview:iadBanner];
}
ViewController2.h/ViewController2.m same as ViewController1.h/.m
Instead of both UIViewControllers having the ad displayed however, only the second one does. Now I'm guessing that's because they're both referring to the same instance instead of creating another instance of the iadbanner passed from the Singleton class. Does anybody know how to fix this? :)
So I have added iAds to my application and I have done this very simply. By importing iAds into the ViewController.h and putting the following code into the ViewController.m file:
self.canDisplayBannerAds = YES;
This works, however, when I the add loads the screen is squished. Where all of the images and sprites become shorter.
Is there any way the add can just be overlaid on top of the SKScene? so that is does not affect the elements below – so that it stops the squishing?
Also, is there a way to only have the banner ads run in certain SKScenes or when a BOOL is changed from YES to NO?
UPDATED
Code from my project:
#import "XYZViewController.h"
#import "XYZMenuScene.h"
#import <iAd/iAd.h>
#implementation XYZViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Configure the view.
SKView * skView = (SKView *)self.view;
skView.showsFPS = YES;
skView.showsNodeCount = YES;
// Create and configure the scene.
SKScene * scene = [XYZMenuScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
//iAds Enabled
self.canDisplayBannerAds = YES;
// Present the scene.
[skView presentScene:scene];
}
This is all I have in terms of the iAds. It makes the entire scene push upwards to make room for the ad banner. Where I want it to just be over the scene and not push anything. So it covers, instead of pushes.
To overlay you can set the Z position of the banner higher than the rest of the content by default it's 0.
eg:
banner.layer.zPosition=2;
but I believe the iAd framework has built in method thats you can override to control when the ads must display and when they shouldn't and give you much more control.
Specific methods are called when the ad is available,and like you said you make the ads appear only when a boolean value is 1 or 0.
eg:
- (void)bannerViewDidLoadAd:(ADBannerView *)banner
{
if(showADS==1)
{
if (!self.bannerIsVisible)
{
[UIView beginAnimations:#"animateAdBannerOn" context:NULL];
// Assumes the banner view is just off the bottom of the screen.
banner.frame = CGRectOffset(banner.frame, 0, -banner.frame.size.height);
[UIView commitAnimations];
self.bannerIsVisible = YES;
}
}
else
{NSlog(#"No ads");}
}
check apple's documentation regarding this,they are really helpful
https://developer.apple.com/library/ios/documentation/userexperience/Conceptual/iAd_Guide/WorkingwithBannerViews/WorkingwithBannerViews.html
But let give a complete example.
The banner instance is only available in you iAd Methods not outside them.
In your view did load method:
- (void)viewDidLoad
{
self.canDisplayBannerAds=YES;
}
This method is called when the ad is loaded,in this method specify the position of the ad
- (void)bannerViewDidLoadAd:(ADBannerView *)banner
{
banner.layer.zPosition=2;
/*do all the additional like checking is the boolean is 1 or 0 etc.*/
}
If you are working with iAd and sprite kit,you need to call methods in your view controller from the scene class to do all the ad work.
Suppose you have a method named checkAD in your view controller that checks if the ad should shown or not etc..
You can call this method from your scene class by using this code
if([self.delegate respondsToSelector:#selector(checkAD)]){
[self.delegate checkAD];
}
Add the SKSCene in a container View that might help. From my experience any Apple subclasses of UIViewController can do weird stuff when iAds are added through canDisplayBannerAds.
When my app was in development, iAds worked great. Every 30 seconds I would either get a call to "bannerViewDidLoadAd" or to "didFailToReceiveAdWithError" and I prepared the app to handle either callback. I get the green checkmark "You're connected to iAd's App Network" and the other test ads.
Now that the app is live, it only gets "didFailToReceiveAdWithError" and never loads an ad.
I'm running the released version of the app on my phone plugged in to the Xcode Organizer Console, and I see the NSLog that prints within "didFailToReceiveAdWithError"
The iAd Portal doesn't show any requests though, it lists 0 requests.
I've built it to my phone again from XCode with the development profile and again it works as it should. I've deleted the app, shut down my phone, signed out of my iTunes Apple ID, and redownloaded the app from the App Store and still the ad fails every time.
Here's how I've got the ad coded:
In my rootViewController, the user chooses to start a new game, and I animate the new view:
UIViewController *nextController = [[GamePlayViewController alloc] initWithNibName:#"GamePlayView" bundle:nil];
[nextController performSelector:#selector(setDelegate:) withObject:self];
nextController.view.frame = CGRectMake(0, 570, 320, 568);
[self.view addSubview:nextController.view];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:0.23];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(animationDidStop:finished:context:)];
nextController.view.frame = CGRectMake(0, 0, 320, 568);
[UIView commitAnimations];
temporaryController = nextController;
GamePlayViewController.h includes:
- #import <iAd/iAd.h>
- #interface GamePlayViewController : UIViewController <ADBannerViewDelegate, UIDocumentInteractionControllerDelegate> {
GamePlayViewController.m includes:
- ADBannerView *_bannerView;
Once the user is in GamePlayViewController.m, there is an animation triggered in viewDidLoad, and once that animation completes, an ad is called:
if ([ADBannerView instancesRespondToSelector:#selector(initWithAdType:)]) {
_bannerView = [[ADBannerView alloc] initWithAdType:ADAdTypeBanner];
} else {
_bannerView = [[ADBannerView alloc] init];
}
_bannerView.delegate = self;
[self.view bringSubviewToFront:_bannerView];
}
That's really all there is to it other than the callback methods for iAds.
- (void)bannerViewDidLoadAd:(ADBannerView *)banner
{
NSLog(#"ad loaded!");
_bannerView.hidden = NO;
[self layoutAnimated:YES];
}
- (void)layoutAnimated:(BOOL)animated
{
// As of iOS 6.0, the banner will automatically resize itself based on its width.
// To support iOS 5.0 however, we continue to set the currentContentSizeIdentifier appropriately.
CGRect contentFrame = self.view.bounds;
if (contentFrame.size.width < contentFrame.size.height) {
_bannerView.currentContentSizeIdentifier = ADBannerContentSizeIdentifierPortrait;
} else {
_bannerView.currentContentSizeIdentifier = ADBannerContentSizeIdentifierLandscape;
}
CGRect bannerFrame = _bannerView.frame;
if (_bannerView.bannerLoaded) {
bannerFrame.origin.y = 0;
} else {
bannerFrame.origin.y = contentFrame.size.height;
}
[UIView animateWithDuration:animated ? 0.25 : 0.0 animations:^{
_contentView.frame = contentFrame;
[_contentView layoutIfNeeded];
_bannerView.frame = bannerFrame;
[self.view addSubview:_bannerView];
}];
}
- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
NSLog(#"ad failed!");
_bannerView.hidden = YES;
}
Maybe something I am doing is wrong or I should have the ad on the rootViewController itself, but this code works great with test iAds, so I'm not sure why it's not working with the App Store version of the app.
Thanks for any help!
I've not looked at your code but I've observed this also. Sometimes Apple just hasn't sold any ads so there aren't any to display for you. Consider using an ad aggregator. I use https://github.com/larsacus/LARSAdController but modified it to display ads the way I want to.
I cover that in my blog entry: http://www.notthepainter.com/iad-admob-integration-with-a-dynamic-uiview/
Here is the text of the blog entry so it is preserved here, just in case my blog goes away someday.
I’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. They’re Done Yet? and Wheelin. 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.
I’ve forked LARSAdController and submitted the above sample code in a full project on my github.
After a few days, I was getting ready to call for support, and then the iAds started working. So I guess the answer is that there is potential for a lag to occur between an app going live and ads populating for that app.
Initially when the ads started coming in, the fill rate wasn't much above 50% and I was only seeing the one ad for iTunes Radio, but at least the ads had started working. Now fill rate is up to 67% and I'm seeing a bit more of a variety with the ads.
i just implemented a basic iad banner and on the xib file i dragged and dropped the bannerview on the bottom of the iphone and ctrl clicked file owner and dragged it to the adview. The issue is when I load it onto an iphone 5, the banner is only 3/4ths of the way down because the xib view is on iphone 4. Is there anyway i can avoid the xib view altogether and have some code that just puts the banner at the bottom no matter the device. This is my first time using iAds so i'm a bit new to it.
Thanks
In my viewController.m file here is a few lines of code, i'm assuming this is where the code would go if it's possible.
#synthesize adView;
- (void) bannerViewDidLoadAd:(ADBannerView *)banner {
[adView setHidden:NO];
}
- (void) bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error {
[adView setHidden:YES];
}
In viewDidLoad:
adView.frame = CGRectMake(0, self.view.frame.size.height-adView.frame.size.height, adView.frame.size.width, adView.frame.size.height);
Also check out Robotic Cat's comment, as he is correct.
Apple has some sample code that demonstrates the proper way to display banner-style iAds in your app. Check it out here. In particular, take a look at the ContainerBanner project.