I've been struggling with this now for a long time. Where is my mistake? At the moment the hide and show seems to work, but everytime I come "back" to my viewcontroller I see that my view is shifted up, but there is no ad in there. But the first time I see the view controller, there is an ad. What am I doing wrong?
I just want to show the same ad across view controllers and this is like the parent UIViewController class, a lot of other view controllers inherit from:
#pragma mark View lifecycle
-(void)viewDidLoad
{
[super viewDidLoad];
if(![[NSUserDefaults standardUserDefaults] objectForKey:kInAppPurchaseNoAds]){
self.bannerContainer = ((AppDelegate *)[[UIApplication sharedApplication] delegate]).bannerView;
self.bannerContainer.frame = CGRectOffset(self.bannerContainer.frame, 0, self.view.frame.size.height);
[self.view addSubview:self.bannerContainer];
}
}
//Handle the in app purchases
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
//iAd
if(![[NSUserDefaults standardUserDefaults] objectForKey:kInAppPurchaseNoAds] && !self.bannerContainer){
self.bannerContainer.delegate = self;
}
if(self.bannerContainer.bannerLoaded){
[self showBanner];
}
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(checkIAdPurchase) name:IAPHelperProductPurchasedNotification object:nil];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self hideBanner];
self.bannerContainer.delegate = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self name:IAPHelperProductPurchasedNotification object:nil];
}
#pragma mark Check iAd purchase
-(void)checkIAdPurchase
{
if([[NSUserDefaults standardUserDefaults] objectForKey:kInAppPurchaseNoAds] && self.bannerContainer){
[self hideBanner];
[self.bannerContainer removeFromSuperview];
self.bannerContainer.delegate = nil;
self.bannerContainer = nil;
}
}
#pragma mark IAd delegate
- (void)bannerViewDidLoadAd:(ADBannerView *)banner
{
[self showBanner];
}
- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
[self hideBanner];
}
#pragma mark Show and hide the banner
- (void)showBanner
{
if(!self.isBannerVisible){
[self.view layoutIfNeeded];
[UIView animateWithDuration:0.5
animations:^{
//Restore the constraint
self.mainViewBottomConstraint.constant = 50;
//Move the banner on
self.bannerContainer.frame = CGRectOffset(self.bannerContainer.frame, 0, -50);
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
self.isBannerVisible = YES;
}];
}
}
- (void)hideBanner
{
if(self.isBannerVisible){
[self.view layoutIfNeeded];
[UIView animateWithDuration:0.5
animations:^{
//Restore the constraint
self.mainViewBottomConstraint.constant = 0;
//Move the banner off
self.bannerContainer.frame = CGRectOffset(self.bannerContainer.frame, 0, self.bannerContainer.frame.size.height);
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
self.isBannerVisible = NO;
}];
}
}
Actually you are creating instance of iAd in AppDelegate and that's only one in the whole app.Now when you first time come to Vc then due to code in viewdidload it add to your Vc..
Now when you move to other view controller that viewcontroller also add iAd at viewdidload..You have only one object of iAd so it will add to at a time in One VC..So when you move to other Vc iAd will remove from that Vc and add to new VC..
Solution : You should call iAD subview code in ViewWillAppear...
Related
I've been developing an iPhone / iPad game using Sprite Kit and in between each round I load an interstitial advert.
The interstitial is loaded on the main GameViewController and sits on top of the skview. I use a series of observers to trigger and cancel adverts and this all seems to work fine.
However, I've noticed some serious memory issues and after 4 or 5 rounds the app will crash. It appears to be directly related to the iAd interstitial. I've attached my code and you can see that I'm deallocating the objects, but the memory foot print does not seem to drop. I am using ARC too.
Does anyone know what could be causing this issue? I did read here: iAd & AdMob Heavy on Memory that the webkit view seems to hold on to its contents. I need to find a way to fix this, my code for my GameViewController is as follows:
#pragma mark - GAME LOAD
-(void)loadStartScreen{
_theView = (SKView *) self.view;
_theView.showsFPS = YES;
_theView.showsNodeCount = YES;
//Sprite Kit applies additional optimizations to improve rendering performance
_theView.ignoresSiblingOrder = YES;
// Create and configure the scene.
_theScene = [MainMenuScene sceneWithSize:_theView.bounds.size];
_theScene.scaleMode = SKSceneScaleModeAspectFill;
_theScene.backgroundColor = [UIColor grayColor];
// Present the scene
[_theView presentScene:_theScene];
// setup observer
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(requestFullScreenAd) name:#"requestAdvert" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(showFullScreenAd) name:#"showAdvert" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(cancelAdverts) name:#"cancelAdvert" object:nil];
}
#pragma mark - ADVERT CREATION AND SUPPORT
-(void)requestFullScreenAd {
// run the process on the main thread in a background queue
dispatch_async(bGQueue, ^{
if (_requestingAd == NO) {
_interstitial = [[ADInterstitialAd alloc]init];
_interstitial.delegate = self;
self.interstitialPresentationPolicy = ADInterstitialPresentationPolicyManual;
NSLog(#"Ad Request");
_requestingAd = YES;
}
});
}
-(void)showFullScreenAd{
if (_adLoaded) {
CGRect interstitialFrame = self.view.bounds;
interstitialFrame.origin = CGPointMake(0, 0);
_adView = [[UIView alloc] initWithFrame:interstitialFrame];
[self.view addSubview:_adView];
[_interstitial presentInView:_adView];
_button = [UIButton buttonWithType:UIButtonTypeCustom];
[_button addTarget:self action:#selector(closeAd:) forControlEvents:UIControlEventTouchDown];
_button.backgroundColor = [UIColor clearColor];
[_button setBackgroundImage:[UIImage imageNamed:kCloseAd] forState:UIControlStateNormal];
_button.frame = CGRectMake(10, 10, 40, 40);
_button.alpha = 0.75;
[_adView insertSubview:_button aboveSubview:_adView];
[UIView beginAnimations:#"animateAdBannerOn" context:nil];
[UIView setAnimationDuration:1];
[_adView setAlpha:1];
[UIView commitAnimations];
}
}
-(void)closeAd:(id)sender {
[UIView beginAnimations:#"animateAdBannerOff" context:nil];
[UIView setAnimationDuration:1];
[_adView setAlpha:0];
[UIView commitAnimations];
_adView=nil;
_requestingAd = NO;
_button = nil;
_interstitial.delegate = nil;
_interstitial = nil;
// notification for ad complete
[[NSNotificationCenter defaultCenter] postNotificationName:#"adClosed" object:nil];
}
-(void)cancelAdverts{
[_interstitial cancelAction];
_adView=nil;
_requestingAd = NO;
_button = nil;
_interstitial.delegate = nil;
_interstitial = nil;
}
#pragma mark - IAD DELEGATE
-(void)interstitialAd:(ADInterstitialAd *)interstitialAd didFailWithError:(NSError *)error {
[_interstitial cancelAction];
_adView=nil;
_requestingAd = NO;
_button = nil;
_interstitial.delegate = nil;
_interstitial = nil;
NSLog(#"Ad didFailWithERROR");
NSLog(#"%#", error);
// request another advert if it failed
//[self requestFullScreenAd];
}
-(void)interstitialAdDidLoad:(ADInterstitialAd *)interstitialAd {
if (interstitialAd.loaded) {
_adLoaded = YES;
[[NSNotificationCenter defaultCenter]postNotificationName:#"adLoaded" object:nil];
}
NSLog(#"Ad DidLOAD");
}
-(void)interstitialAdDidUnload:(ADInterstitialAd *)interstitialAd {
[self closeAd:nil];
NSLog(#"Ad DidUNLOAD");
}
-(void)interstitialAdActionDidFinish:(ADInterstitialAd *)interstitialAd {
[self closeAd:nil];
NSLog(#"Ad DidFINISH");
}
Then in my level complete SKScene:
#pragma mark - SCENE APPEARS
-(void)didMoveToView:(SKView *)view {
// request an advert if advert removal is not purchased
if (![[[UserDetails sharedManager]iapAdsRemoved]boolValue]) {
// send request ad notification
[[NSNotificationCenter defaultCenter]postNotificationName:#"requestAdvert" object:nil];
// look for add loaded notification
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(adLoaded) name:#"adLoaded" object:nil];
// look for add completed
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(adShowCompleted) name:#"adClosed" object:nil];
}
// setup UI
[self createUI];
if (![[UnlockController sharedManager]allunlocksOpen]) {
// check all unlocks
[[UnlockController sharedManager]checkUnlocks:[[UserDetails sharedManager]userTotalScore]+[[UserDetails sharedManager]userLevelScore]];
// get the next unlock
[self getNextUnlockScore];
// set bar with correct increment
[unlockBar setBarValues:[[UserDetails sharedManager]userTotalScore]+[[UserDetails sharedManager]userLevelScore] increment:[[UserDetails sharedManager]userTotalScore] nextObject:nextScore];
}
else{
[self allUnlocksOpen];
}
// pre add button
preAdButtonPress = 3;
// variables
startCount = 0;
unlockItemsCount = 0;
allUnlocks = [[UnlockController sharedManager]finalUnlockOpen];
// start unlocks sequence
[self performSelector:#selector(runRewards) withObject:nil afterDelay:1.0];
}
-(void)willMoveFromView:(SKView *)view{
// cancel any adverts
[[NSNotificationCenter defaultCenter]postNotificationName:#"cancelAdvert" object:nil];
// remove observers
[[NSNotificationCenter defaultCenter]removeObserver:#"adClosed"];
[[NSNotificationCenter defaultCenter]removeObserver:#"adLoaded"];
}
Was a memory issue with some core code rather than the iad causing memory leaks.
I'm trying to handle my iAd via a singleton, since I'm using these banners in several view controllers. Now I'm confused of what do these objects store, since I move them around differently on each view controller when an ad is shown or if an error occured. Here my code:
Singleton:
+ (MySingleton *)sharedInstance {
static dispatch_once_t once;
static MySingleton * sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (id)init
{
if (self = [super init]) {
if ([ADBannerView instancesRespondToSelector:#selector(initWithAdType:)]) {
self.bannerView = [[ADBannerView alloc] initWithAdType:ADAdTypeBanner];
} else {
self.bannerView = [[ADBannerView alloc] init];
}
}
return self;
}
And here how it is initalized:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
//iAd
if(![[NSUserDefaults standardUserDefaults] objectForKey:kInAppPurchaseNoAds]){
self.bannerView = [MySingleton sharedInstance].bannerView;
self.bannerView.delegate = self;
self.bannerView.frame = CGRectOffset(self.bannerView.frame, 0, self.view.frame.size.height);
[self.view addSubview:self.bannerView];
}
}
And the delegate methods:
- (void)showBanner
{
if(!self.isBannerVisible){
[self.view layoutIfNeeded];
[UIView animateWithDuration:0.5
animations:^{
//Restore the constraint
self.mainContainerToSuperviewConstraint.constant = 50;
//Move the banner on
self.bannerView.frame = CGRectOffset(self.bannerView.frame, 0, -50);
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
self.isBannerVisible = YES;
}];
}
}
- (void)hideBanner
{
if(self.isBannerVisible){
[self.view layoutIfNeeded];
[UIView animateWithDuration:0.5
animations:^{
//Restore the constraint
self.mainContainerToSuperviewConstraint.constant = 0;
//Move the banner off
self.bannerView.frame = CGRectOffset(self.bannerView.frame, 0, self.bannerView.frame.size.height);
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
self.isBannerVisible = NO;
}];
}
}
- (void)bannerViewDidLoadAd:(ADBannerView *)banner
{
[self showBanner];
}
- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
[self hideBanner];
}
Now what I'm confused is, do I have to check the position of the banner view again if the user was in another view with also an iAd where the rectangle with visible banner was at let's say 75 from the bottom of the screen and not 50? Or do these positions do not influence the AdBannerView but only the single object in each class?! I mean if he was in the other view and there the code moved the Banner to 75 pixels from the bottom, are these 75 pixels stored in my singleton AdView? So the original view had the banner now at 75 and not at 50?
I would suggest against having a singleton for a view - one view instance can be a subview only of 1 view, so you need to track adding/removing it and also you need to set the frame every time you add it again as a subview. You better have some kind of base view controller to share the instantiation logic and the control of the banners.
I am trying to add iads to my new sprite kit game. The problem is that i do not need the ad to be on all the scenes. I've started to create an ADBannerView in the mainstoryboard. After that i'm trying to use NSNotification to hide and show the ads in different scenes, but its not working. the ad is still showing even though i've added into Menu.m(scene):
[[NSNotificationCenter defaultCenter] postNotificationName:#"hideAd" object:nil];
ViewController.m
-(void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
// Configure the view.
SKView * skView = (SKView *)self.view;
//skView.showsFPS = YES;
//skView.showsNodeCount = YES;
//skView.showsPhysics = YES;
if (!skView.scene) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleNotification:) name:#"hideAd" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleNotification:) name:#"showAd" object:nil];
SKScene * scene = [Menu sceneWithSize:skView.bounds.size];
NSLog(#"%#", scene);
// Present the scene.
[skView presentScene:scene];
}
}
-(void)bannerViewDidLoadAd:(ADBannerView *)banner {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1];
[banner setAlpha:1];
[UIView commitAnimations];
}
-(void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1];
[banner setAlpha:0];
[UIView commitAnimations];
}
- (void)handleNotification:(NSNotification *)notification
{
if ([notification.name isEqualToString:#"hideAd"]) {
[self hidesBanner];
}else if ([notification.name isEqualToString:#"showAd"]) {
[self showsBanner];
}
}
-(void)hidesBanner {
NSLog(#"HIDING BANNER");
[adView setAlpha:0];
}
-(void)showsBanner {
NSLog(#"SHOWING BANNER");
[adView setAlpha:1];
}
It is not good to create an iAd if you do not intend to show it.
According to Apple's Banner View Best Practices:
• Only create a banner view when you intend to display it to the user. Otherwise, it may cycle through ads and deplete the list of available advertising for your app.
• If the user navigates from a screen of content with a banner view to a screen that does not have a banner view, and you expect them to be on that screen for a long period of time, remove the banner view from the view hierarchy, set its delegate to nil and release it before transitioning to the new screen of content. More generally, avoid keeping a banner view around when it is invisible to the user.
I remember reading about a hidden property a while back but looking at the iAd Framework Reference, I cannot find any such property. I recommend you follow Apple's guidelines if you do not want to display an ad in a specific scene.
I'm trying to create a simple modal view controller that lets you edit text using a text view. However, when I present the view controller modally, it slides in from the bottom left direction instead of just sliding in from the bottom.
Here's a video of the weird effect: http://youtu.be/9M_MHA5mt1M
My controller simply watches for the keyboard to show and then resizes the text view using auto layout appropriately. Here's the code:
#import "TextPicker.h"
#interface TextPicker ()
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *keyboardHeight;
#end
#implementation TextPicker
- (id)initWithText:(NSString *)text
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:nil];
self = [storyboard instantiateViewControllerWithIdentifier:#"textPicker"];
if (self) {
self.text = text;
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self observeKeyboard];
//self.textView.text = self.text;
[self.textView becomeFirstResponder];
}
- (void) viewWillDisappear:(BOOL)animated {
[self.textView resignFirstResponder];
}
- (IBAction)savePressed:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)cancelPressed:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (void) dealloc {
[self stopObervingKeyboard];
}
- (void)observeKeyboard {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillChangeFrameNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
- (void)stopObervingKeyboard {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillChangeFrameNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
- (void)keyboardWillShow:(NSNotification *)notification {
NSDictionary *info = [notification userInfo];
NSValue *kbFrame = [info objectForKey:UIKeyboardFrameEndUserInfoKey];
CGRect keyboardFrame = [kbFrame CGRectValue];
self.keyboardHeight.constant = -keyboardFrame.size.height;
NSTimeInterval animationDuration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
[UIView animateWithDuration:animationDuration animations:^{
[self.view layoutIfNeeded];
}];
}
- (void)keyboardWillHide:(NSNotification *)notification {
NSDictionary *info = [notification userInfo];
NSTimeInterval animationDuration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
self.keyboardHeight.constant = 0;
[UIView animateWithDuration:animationDuration animations:^{
[self.view layoutIfNeeded];
}];
}
- (IBAction)dismissKeyboard:(id)sender {
[self.textView resignFirstResponder];
}
#end
Your view is animating as you have asked it to by wrapping the [self.view layoutIfNeeded] call inside an animation block.
In viewDidLoad you begin observing keyboard changes, and when you detect them you animate the adjustments, this is normally correct. But then, before the view does its first layout, you show the keyboard; this results in an animation for all the views from CGRectZero to their proper sizes. And this is the effect you are seeing.
So basically you need to give the view a chance to layout before your animated layoutIfNeeded call. Probably the easiest way to do this is simply to move [self.textView becomeFirstResponder]; to either viewWillAppear: or viewDidAppear:.
*As a side note, remember to call super in appearance calls. I noticed you did not call [super viewWillDisappear];.
I have a simple view controller (SecondViewController) used to manage a UITextview
(I'm building a simple editor)
this is the code of the SecondViewController.h
#interface SecondViewController : UIViewController {
IBOutlet UITextView *textView;
}
#property (nonatomic,retain) IBOutlet UITextView *textView;
#end
and this is the SecondViewController.m
//
// EditorViewController.m
// Editor
//
// Created by elio d'antoni on 13/01/11.
// Copyright 2011 none. All rights reserved.
//
#implementation SecondViewController
#synthesize textView;
/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
}
*/
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"uiViewBg.png"]];
textView.layer.borderWidth=1;
textView.layer.cornerRadius=5;
textView.layer.borderColor=[[UIColor darkGrayColor] CGColor];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillAppear:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillDisappear:) name:UIKeyboardWillHideNotification object:nil];
}
-(void) matchAnimationTo:(NSDictionary *) userInfo {
NSLog(#"match animation method");
[UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
[UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
}
-(CGFloat) keyboardEndingFrameHeight:(NSDictionary *) userInfo {
NSLog(#"keyboardEndingFrameHeight method");
CGRect keyboardEndingUncorrectedFrame =
[[ userInfo objectForKey:UIKeyboardFrameEndUserInfoKey ]
CGRectValue];
CGRect keyboardEndingFrame =
[self.view convertRect:keyboardEndingUncorrectedFrame
fromView:nil];
return keyboardEndingFrame.size.height;
}
-(CGRect) adjustFrameHeightBy:(CGFloat) change
multipliedBy:(NSInteger) direction {
NSLog(#"adjust method");
return CGRectMake(20,
57,
self.textView.frame.size.width,
self.textView.frame.size.height + change * direction);
}
-(void)keyboardWillAppear:(NSNotification *)notification {
NSLog(#"keyboard appear");
[UIView beginAnimations:nil context:NULL];
[self matchAnimationTo:[notification userInfo]];
self.textView.frame =
[self adjustFrameHeightBy:[self keyboardEndingFrameHeight:
[notification userInfo]]
multipliedBy:-1];
[UIView commitAnimations];
}
-(void)keyboardWillDisappear:(NSNotification *) notification {
NSLog(#"keyboard disappear");
[UIView beginAnimations:nil context:NULL];
[self matchAnimationTo:[notification userInfo]];
self.textView.frame =
[self adjustFrameHeightBy:[self keyboardEndingFrameHeight:
[notification userInfo]]
multipliedBy:1];
[UIView commitAnimations];
}
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
(void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
(void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
(void)dealloc {
[super dealloc];
}
#end
the problem is that if load the view controller from a tab bar controller the textView doesn't resize when the keyboard appear, but the SAME code works if loaded as a single view based app. I hope I was clear enough.
I used the tabBar template provided by xcode no modifications.
Your code looks right, did notice you are not releasing "textView" in dealloc and "viewDidUnload" but this should make any difference.
I'd be checking the notications are received and everthing is wired up ie textView is not nil