iOS: iAd Singleton Confusion - ios

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.

Related

iAd is white (not loaded) when application starts

My iAd is white. It looks like bannerViewDidLoadAd is run when the iAd is not fully loaded. It happens only when I show my ViewController first time (application starts). When I go to another controller and return, iAd is loaded properly. Do you have any idea why?
- (void)bannerViewDidLoadAd:(ADBannerView *)banner {
_bannerView.hidden = NO;
[self.view setNeedsLayout];
}
- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error {
_bannerView.hidden = YES;
[self.view setNeedsLayout];
}
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (!_bannerView) {
_bannerView = [[ADBannerView alloc] initWithAdType:ADAdTypeBanner];
_bannerView.delegate = self;
_bannerView.hidden = YES;
CGRect bannerFrame = _bannerView.frame;
bannerFrame.origin.y = self.view.bounds.size.height - _bannerView.bounds.size.height;
_bannerView.frame = bannerFrame;
[self.view addSubview:_bannerView];
}
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
_bannerView.delegate = nil;
[_bannerView removeFromSuperview];
_bannerView = nil;
}

iAd across view controllers (Singleton): Somehow not working

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...

Move UIImageView from A to B

I need to move an UIImageView from x,y to x,y1.
This is what i have. I tried lots of different codes in -(void)animation, most using animateWithDuration but none seems to work. It just doesn't do anything. I must be missing something. Is .h correct? How would you make the image move? I then have to loop that, how can i do that? It's my first time with animate, I can't figure out what to do, it has to be simple. Thanks in Advance.
in .h I connected it this way
#import <UIKit/UIKit.h>
#interface ImageBeaconViewController : UIViewController {
__weak IBOutlet UIImageView *blueView;
}
#end
the in .m I've got
- (void)viewDidLoad
{
[self animation];
[super viewDidLoad];
}
....
- (void)animation{
[UIView animateWithDuration:3 animations:^{
blueView.alpha = 1;
blueView.center = CGPointMake(blueView.center.x , blueView.center.y +??);
}];
}
??= I still have to decide how much I want it to move
This way the app crashes as soon as I get to the screen with the animation
Add Method Like This:
- (void) animateToPoint:(CGPoint)point {
[UIView animateWithDuration:3 animations:^{
blueView.alpha = 1;
blueView.center = point;
}];
}
Call like this:
- (void) viewDidAppear:(BOOL)animated {
int distanceToSlide = 40; // will slide down 40px (use -40 to go up)
CGPoint targetPoint = CGPointMake(blueView.center.x, blueView.center.y + distanceToSlide);
[self animateToPoint:targetPoint];
}
NOTE
Notice that I put your animation call in viewDidAppear if you animate in viewDidLoad you won't see anything because your view isn't displayed yet.
If it's still not moving, make sure "useAutolayout" is not selected check this:
Just because I already built it, try this for tap to animate:
In your viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc]init];
tap.numberOfTapsRequired = 1;
[tap addTarget:self action:#selector(handleTap:)];
[self.view addGestureRecognizer:tap];
}
Then, use these:
- (void) handleTap:(UITapGestureRecognizer *)tap {
int distanceToSlide = 40; // will slide down 40px (use -40 to go up)
CGPoint targetPoint = CGPointMake(blueView.center.x, blueView.center.y + distanceToSlide);
[self animateToPoint:targetPoint];
}
- (void) animateToPoint:(CGPoint)point {
[UIView animateWithDuration:3 animations:^{
blueView.alpha = 1;
blueView.center = point;
}];
}
Here's a way to loop it:
#implementation ImageBeaconViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
pointA = CGPointMake(40, 40);
pointB = CGPointMake(250, 350);
blueView.center = pointA;
}
- (void) viewDidAppear:(BOOL)animated {
[self animate];
}
- (void) animate {
if (CGPointEqualToPoint(_blueView.center, pointA)) {
[self animateToPoint:pointB];
}
else {
[self animateToPoint:pointA];
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void) animateToPoint:(CGPoint)point {
[UIView animateWithDuration:1.0 animations:^{
_blueView.alpha = 1;
_blueView.center = point;
} completion:^(BOOL finished) {
if (finished) {
[self animate];
}
}];
}
#end
[UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
blueView.alpha = 1;
blueView.center = CGPointMake(blueView.center.x , blueView.center.y + 100);
} completion:^(BOOL finished) {
//if you need any thing after animation is done
}];
this is them code for animation i have

UITableView scrolling under iAd

i want to display iAd at the bottom of the screen..
i tried to add subView but the iAd showed in a cell and he hides him.
what should i do to make the tableView to scroll under the iAd?
my code:
- (void)viewDidLoad
{
[super viewDidLoad];
[self createAdBannerView];
[self.view addSubview:self.adBannerView];
}
#pragma mark - ADBannerViewDelegate
- (void)bannerViewDidLoadAd:(ADBannerView *)banner
{
[self adjustBannerView];
}
- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
[self adjustBannerView];
}
- (BOOL)bannerViewActionShouldBegin:(ADBannerView *)banner willLeaveApplication:(BOOL)willLeave
{
return YES;
}
- (void)bannerViewActionDidFinish:(ADBannerView *)banner
{
}
- (void) adjustBannerView
{
CGRect contentViewFrame = self.view.bounds;
CGRect adBannerFrame = self.adBannerView.frame;
if([self.adBannerView isBannerLoaded])
{
CGSize bannerSize = [ADBannerView sizeFromBannerContentSizeIdentifier:self.adBannerView.currentContentSizeIdentifier];
contentViewFrame.size.height = contentViewFrame.size.height - bannerSize.height;
adBannerFrame.origin.y = contentViewFrame.size.height;
}
else
{
adBannerFrame.origin.y = contentViewFrame.size.height;
}
[UIView animateWithDuration:0.5 animations:^{
self.adBannerView.frame = adBannerFrame;
self.contentView.frame = contentViewFrame;
}];
}
- (void) createAdBannerView
{
self.adBannerView = [[ADBannerView alloc] initWithFrame:CGRectZero];
CGRect bannerFrame = self.adBannerView.frame;
bannerFrame.origin.y = self.view.frame.size.height;
self.adBannerView.frame = bannerFrame;
self.adBannerView.delegate = self;
self.adBannerView.requiredContentSizeIdentifiers = [NSSet setWithObjects:ADBannerContentSizeIdentifierPortrait, ADBannerContentSizeIdentifierLandscape, nil];
}
EDIT:
How do I prevent this?
i want to keep the iAd at the botton..
thanks in advance!
If you're using auto layout, what I've done before is add an outlet to the constraint for the bottom space to superview (assuming you have a tableview within a view).
You can then just adjust the constraint in the callback when the ad is loaded by for example doing
tableViewBottomSpaceConstraintOutlet.constant -= adBannerView.frame.size.height;
where tableViewBottomSpaceConstraintOutlet is the outlet on the bottom space to superview constraint and adBannerView is your ad banner's view.
Suggestion from Apple is to add a container view to contain the ScrollView(TableView) and setup constraints between the fixed view and the container view
details here:
Using Scroll Views with Auto Layout

How to show app-specific status bar at bottom of screen, outside of app?

I have a requirement to show a status bar at certain times at the bottom of my application. I can easily put this at the bottom of my application's main view, but whenever I push a view controller on top of this (either modally or not) it hides this status bar.
Is there any way I can add a status bar like this, and have it be outside the bounds of my application itself? Ideally I'd like this to work like the call-in-progress status bar on the iPhone - when this bar appears, the app is pushed down, and a call to [[UIScreen mainScreen] applicationFrame] returns the correct size (i.e. it accounts for the presence of this status bar when calculating the height available for the app).
I wanted to do this, too, so I tried View Controller Containment. I'm still trying it out, so I'm not willing to give this a ringing endorsement, but it might be something you'd want to try playing around with yourself if you're in iOS5. But it appears to give you a status bar that will appear or disappear from the bottom of the screen.
This is a view controller that will open another view controller, but if there is status text to show, it pops up from the bottom of the screen and stays there until you get rid of it. I've only done a little testing so far, but it looks like this handles pushViewController/popViewController, but maybe not modal views.
My header looks like:
// StatusBarViewController.h
//
// Created by Robert Ryan on 7/8/12.
#import <UIKit/UIKit.h>
#interface StatusBarViewController : UIViewController
#property (strong, nonatomic) UIViewController *appController;
- (void)setStatus:(NSString *)text;
#end
My implementation file (this is ARC) looks like:
// StatusBarViewController.m
//
// Created by Robert Ryan on 7/8/12.
#import "StatusBarViewController.h"
#interface StatusBarViewController ()
{
BOOL _statusHidden;
UIView *_appView;
UILabel *_statusLabel;
}
#end
#implementation StatusBarViewController
#synthesize appController = _appController;
- (void)dealloc
{
_appView = nil;
_statusLabel = nil;
[self setAppController:nil]; // usually I don't like setters in dealloc, but this does some special stuff
}
- (void)createControlsWithStatusHidden
{
// create default app view that takes up whole screen
CGRect frame = self.view.frame;
frame.origin = CGPointMake(0.0, 0.0);
_appView = [[UIView alloc] initWithFrame:frame];
_appView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_appView.clipsToBounds = YES;
[self.view addSubview:_appView];
// create status label that is just off screen below the app view
_statusLabel = [[UILabel alloc] init];
_statusLabel.font = [UIFont fontWithName:#"Helvetica-Bold" size:12.0];
_statusLabel.backgroundColor = [UIColor darkGrayColor];
_statusLabel.textColor = [UIColor whiteColor];
CGSize size = [#"Hey!" sizeWithFont:_statusLabel.font]; // test size of box with random text
_statusLabel.frame = CGRectMake(0.0, frame.size.height, frame.size.width, size.height);
_statusLabel.textAlignment = UITextAlignmentCenter;
_statusLabel.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth;
[self.view addSubview:_statusLabel];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self createControlsWithStatusHidden];
_statusHidden = YES;
// I'm instantiating from storyboard. If you're using NIBs, just create your controller controller using initWithNib and then set our appController accordingly.
self.appController = [self.storyboard instantiateViewControllerWithIdentifier:#"MainNavigator"];
}
- (void)setAppController:(UIViewController *)controller
{
if (controller)
{
controller.view.frame = CGRectMake(0.0, 0.0, _appView.frame.size.width, _appView.frame.size.height);
[self addChildViewController:controller];
[controller didMoveToParentViewController:self];
if (self.appController)
{
// if we have both a new controller and and old one, then let's transition, cleaning up the old one upon completion
[self transitionFromViewController:self.appController
toViewController:controller
duration:0.5
options:UIViewAnimationOptionTransitionCrossDissolve | UIViewAnimationOptionCurveEaseInOut
animations:nil
completion:^(BOOL finished){
if (self.appController)
{
[self.appController willMoveToParentViewController:nil];
[self.appController removeFromParentViewController];
}
}];
}
else
{
// if we have no previous controller (i.e. this is our first rodeo), then just add it to the view
[_appView addSubview:controller.view];
}
}
else
{
// no new controller, so we're just removing any old on if it was there
if (self.appController)
{
// if there was an old controller, remove it's view, and remove it from the view controller hierarchy
[self.appController.view removeFromSuperview];
[self.appController willMoveToParentViewController:nil];
[self.appController removeFromParentViewController];
}
}
_appController = controller;
}
- (void)hideStatusWithCompletion:(void (^)(BOOL finished))completion
{
[UIView animateWithDuration:0.25
animations:^{
CGRect labelFrame = _statusLabel.frame;
labelFrame.origin.y += labelFrame.size.height;
_statusLabel.frame = labelFrame;
CGRect appFrame = _appView.frame;
appFrame.size.height += labelFrame.size.height;
_appView.frame = appFrame;
}
completion:completion];
}
- (void)unhideStatusWithCompletion:(void (^)(BOOL finished))completion
{
[UIView animateWithDuration:0.25
animations:^{
CGRect labelFrame = _statusLabel.frame;
labelFrame.origin.y -= labelFrame.size.height;
_statusLabel.frame = labelFrame;
CGRect appFrame = _appView.frame;
appFrame.size.height -= labelFrame.size.height;
_appView.frame = appFrame;
}
completion:completion];
}
- (void)setStatus:(NSString *)text
{
BOOL hasText = (text && [text length] > 0);
if (hasText)
{
if (!_statusHidden)
{
// if we have text, but status is already shown, then hide it and unhide it with new value
[self hideStatusWithCompletion:^(BOOL finished){
_statusLabel.text = text;
[self unhideStatusWithCompletion:nil];
}];
}
else
{
// if we have text, but no status is currently shown, then just unhide it
_statusLabel.text = text;
[self unhideStatusWithCompletion:nil];
}
_statusHidden = NO;
}
else
{
if (!_statusHidden)
{
// if we don't have text, but status bar is shown, then just hide it
[self hideStatusWithCompletion:^(BOOL finished){
_statusLabel.text = text;
}];
_statusHidden = YES;
}
}
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
#end
And then, any view controller that wants to update the status message would use a method kind of like:
- (void)setStatus:(NSString *)text
{
UIViewController *controller = [UIApplication sharedApplication].delegate.window.rootViewController;
if ([controller isKindOfClass:[StatusBarViewController class]])
{
[(StatusBarViewController *)controller setStatus:text];
}
}

Resources