AdMob in all my view controllers? - iphone

I have implemented AdMob & everything seems to work,
But i wonderd, how can i put the banner in all of my view controllers?
For now, i have the banner only on the RootViewController.
I have total of 4 view controllers.
Thanks.

What you want here is a GADBannerView singleton of sorts. You can probably create a wrapping class to act as a singleton for your adView, so something like:
#interface GADMasterViewController : UIViewController {
GADBannerView *adBanner_;
BOOL didCloseWebsiteView_;
BOOL isLoaded_;
id currentDelegate_;
}
And just make sure that GADMasterViewController always returns a singleton:
+(GADMasterViewController *)singleton {
static dispatch_once_t pred;
static GADMasterViewController *shared;
// Will only be run once, the first time this is called
dispatch_once(&pred, ^{
shared = [[GADMasterViewController alloc] init];
});
return shared;
}
Have a method which resets the current view controller that's holding on to the adView:
-(void)resetAdView:(UIViewController *)rootViewController {
// Always keep track of currentDelegate for notification forwarding
currentDelegate_ = rootViewController;
// Ad already requested, simply add it into the view
if (isLoaded_) {
[rootViewController.view addSubview:adBanner_];
} else {
adBanner_.delegate = self;
adBanner_.rootViewController = rootViewController;
adBanner_.adUnitID = kSampleAdUnitID;
GADRequest *request = [GADRequest request];
[adBanner_ loadRequest:request];
[rootViewController.view addSubview:adBanner_];
isLoaded_ = YES;
}
}
Then displaying your ad is just a matter of:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
shared = [GADMasterViewController singleton];
[shared resetAdView:self];
}
You probably need to set up a delegate to forward notifications as well since the AdMob SDK doesn't act well to delegates changing on it in the middle of a request.
You can find a blog post about this here.

I don't know how adMob works but like everything else you can create a BaseViewController in which you can add your adMob(in the viewDidLoad method) and then all the other viewControllers can subClass this BaseViewController. just call [super viewDidLoad]; in the viewDidLoad methods of your viewControllers and you will have it...
hoping this sorts your problem... :)

Related

Where to put the UIAlert? [duplicate]

I want to check the pasteboard and show an alert if it contains specific values when the view appears. I can place the code into viewDidLoad to ensure it's only invoked once, but the problem is that the alert view shows too quickly. I know I can set a timer to defer the alert's appearance, but it's not a good work-around I think.
I checked the question iOS 7 - Difference between viewDidLoad and viewDidAppear and found that there is one step for checking whether the view exists. So I wonder if there's any api for doing this?
Update: The "only once" means the lifetime of the view controller instance.
There is a standard, built-in method you can use for this.
Objective-C:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if ([self isBeingPresented] || [self isMovingToParentViewController]) {
// Perform an action that will only be done once
}
}
Swift 3:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if self.isBeingPresented || self.isMovingToParentViewController {
// Perform an action that will only be done once
}
}
The call to isBeingPresented is true when a view controller is first being shown as a result of being shown modally. isMovingToParentViewController is true when a view controller is first being pushed onto the navigation stack. One of the two will be true the first time the view controller appears.
No need to deal with BOOL ivars or any other trick to track the first call.
rmaddy's answers is really good but it does not solve the problem when the view controller is the root view controller of a navigation controller and all other containers that do not pass these flags to its child view controller.
So such situations i find best to use a flag and consume it later on.
#interface SomeViewController()
{
BOOL isfirstAppeareanceExecutionDone;
}
#end
#implementation SomeViewController
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if(isfirstAppeareanceExecutionDone == NO) {
// Do your stuff
isfirstAppeareanceExecutionDone = YES;
}
}
#end
If I understand your question correctly, you can simply set a BOOL variable to recognize that viewDidAppear has already been called, ex:
- (void)viewDidAppear {
if (!self.viewHasBeenSet) { // <-- BOOL default value equals NO
// Perform whatever code you'd like to perform
// the first time viewDidAppear is called
self.viewHasBeenSet = YES;
}
}
This solution will call viewDidAppear only once throughout the life cycle of the app even if you create the multiple object of the view controller this won't be called after one time. Please refer to the rmaddy's answer above
You can either perform selector in viewDidLoad or you can use dispatch_once_t in you viewDidAppear. If you find a better solution then please do share with me. This is how I do the stuff.
- (void)viewDidLoad {
[super viewDidLoad];
[self performSelector:#selector(myMethod) withObject:nil afterDelay:2.0];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
static dispatch_once_t once;
dispatch_once(&once, ^{
//your stuff
[self myMethod];
});
}
By reading other comments (and based on #rmaddy 's answer), I know this is not what OP asked for, but for those who come here because of title of the question:
extension UIViewController {
var isPresentingForFirstTime: Bool {
return isBeingPresented() || isMovingToParentViewController()
}
}
UPDATE
You should use this method in viewDidAppear and viewWillAppear. (thanks to #rmaddy)
UPDATE 2
This method only works with modally presented view controllers and pushed view controllers. it's not working with a childViewController. using didMoveToParentViewController would be better with childViewControllers.
You shouldn't have issues in nested view controllers with this check
extension UIViewController {
var isPresentingForFirstTime: Bool {
if let parent = parent {
return parent.isPresentingForFirstTime
}
return isBeingPresented || isMovingFromParent
}
}
Try to set a BOOL value, when the situation happens call it.
#interface AViewController : UIViewController
#property(nonatomic) BOOL doSomeStuff;
#end
#implementation AViewController
- (void) viewWillAppear:(BOOL)animated
{
if(doSomeStuff)
{
[self doSomeStuff];
doSomeStuff = NO;
}
}
in somewhere you init AViewController instance:
AddEventViewController *ad = [AddEventViewController new];
ad.doSomeStuff = YES;
Not sure why you do this in ViewDidAppear? But if you want doSomeStuff is private and soSomeStuff was called only once, here is another solution by notification:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(doSomeStuff) name:#"do_some_stuff" object:nil];
- (void) doSomeStuff
{}
Then post when somewhere:
[[NSNotificationCenter defaultCenter] postNotificationName:#"do_some_stuff" object:nil];
swift 5
I've tried isBeingPresented() or isMovingToParent.
But It doesn't work.
So I tried below code. and It's work for me!
override func viewDidAppear(_ animated: Bool) {
if (self.isViewLoaded) {
// run only once
}
}
You can use this function in ViewDidLoad method
performSelector:withObject:afterDelay:
it will call that function after delay. so you don't have to use any custom timer object.
and For once you can use
dispatch_once DCD block.Just performSelector in the dispatch_once block it will call performSelector only once when ViewDidLoad is called
Hope it helps

Put FBLoginView in ViewDidLoad crash the app

I have put Facebook loginview code like this
(void)viewDidLoad {
[super viewDidLoad];
// Create Login View so that the app will be granted "status_update" permission.
loginview = [[FBLoginView alloc] init];
loginview.frame = CGRectOffset(loginview.frame, 5, 5);
loginview.delegate = self;
[self.view addSubview:loginview];
[loginview sizeToFit];
}
on first time when i logged in it works fine.But after logged in when i jump to this view from another view it crash the app.
What is the problem?I didn't get it.
I had this same problem. I fixed it by doing this:
-(void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
// This isn't public, so you need to use performSelector...
[fbLoginView performSelector:#selector(unwireViewForSession)];
}
This is based on a bug report here:
https://github.com/buzztabapp/facebook-ios-sdk/commit/2301de144958a5502f9dae0a1bb7c234873581ba
maybe you could try this:
-(void)viewDidLoad
{
...
static id sharedInstance = self;
loginview.delegate = sharedInstance;
...
}
By creating your delegate view instance as static, you store it in the same scope than the facebook login view (which is created in static scope). You avoid bad access error.
I have quit a same problem while dismissing ViewController with FBLoginView.
I solved this problem by setting FBLoginView delegate to nil before calling dismissViewControllerAnimated:completion:
...
self.loginFBView.delegate = nil;
[self dismissViewControllerAnimated:NO completion:nil];

How to release/deallocate/return-from a viewcontroller class that have been popped?

I have a UINavigationController in my application where I am pushing a view controller. On that viewcontroller, there is a preloader view that shows animation while the data is being downloaded and parsed in the background. Below the animation is a button that should redirect the user to some other view controller.
Now, I can accomplish this by popping the viewcontroller since the view I need to be directed to is the view the current view was pushed from. The problem here, however, is that the downloading, parsing, timer, etc.. everything keeps happening in the background.
The functionality I need is for the viewcontroller to completely stop working as soon as I pop the VC by clicking on the button.
Thanks in advance.
What are you using to run the background operations?
You can simplify background operations by subclassing NSOperation and using NSOperationQueue to run it. You just have to release NSOperationQueue and iOS will clean up for you automatically.
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/NSOperation_class/Reference/Reference.html
https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/NSOperationQueue_class/Reference/Reference.html
Here is an example.
#interface MyBackgroundOperation : NSOperation
#end
#implementation MyBackgroundOperation
- (id)init
{
self = [super init];
if(self != nil)
{
// stuff that needs to be done at init
}
return self;
}
- (void)main
{
#try
{
// run my background operations
}
#catch (NSException *e)
{
// catch the exception
}
}
- (void)dealloc
{
// clean up
[super dealloc];
}
#end
Then you start the operation like this
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
MyBackgroundOperation *myOperation = [[MyBackgroundOperation alloc] init];
[operationQueue addOperation:myOperation];
[myOperation release];
[operationQueue release];
The OS will retain the operation until it's finished so that releasing the view controller will not effect it. However, if you want to stop the operation after it's already started you're going to have to use a bit more fancy techniques, maybe notifications or KVO.

iOS Accessing views data from delegate without allocating a new view

I need to change a data (a label) from the app's delegate method ApplicationDidEnterForeground without allocating a new view. The view is called "Reminder", so I imported it into the delegate and I can access its data only if I allocate it (Reminder *anything = [Reminder alloc...etc), but since I want to change the current view loaded I need to have direct access to the view that's already loaded.
How would I do to change the main view's label from the delegate as soon as my application enters foreground?
obs: I know I can do it on -(void)ViewDidLoad or -(void)ViewWillAppear but it won't solve my problem, since it won't change the label if, for example, the user opens the app through a notification box (slide icon when phone is locked). In that case, none of the above methods are called if the app was open in background.
I don't know if I was clear, hope I was. Thank you in advance.
IF you are using storyboards, you can do this to access the current view being seen
- (void)applicationDidEnterBackground:(UIApplication *)application
{
UINavigationController *a=_window.rootViewController;
Reminder *rem = a.topViewController;
rem.label.text=#"test";
}
IF not using story boards
When I create views that I need to access later, I define them as a property, like this
on AppDelegate.h
//#interface SIMCAppDelegate : UIResponder <..........>
//{
//Some variables here
//}
//Properties here
#property (strong, nonatomic) Reminder *reminder;
//Some method declaration here
//eg: -(void) showSomething;
on AppDelegate.m
//#implementation AppDelegate
#synthesize reminder;
so when I alloc/init the view like this
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//codes before here
self.reminder = [[Reminder alloc] init];
self.reminder.label.text = #"OLD LABEL";
//codes after here
}
I will be able to access it again after allocation on other methods, like this
- (void)applicationWillEnterForeground:(UIApplication *)application
{
self.reminder.label.text = #"NEW LABEL";
}
just send a notification from your ApplicationDidEnterForeground: method and receive it on that class where you want to update the label... Like this..
//Your ApplicationDidEnterForeground:
[[NSNotificationCenter defaultCenter] postNotificationWithName:#"UpdateLabel" withObject:nill];
and add observer in it viewDidLoad: of that controller where you want to update label
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(updateLabel:)
name:#"UpdateLabel"
object:nil];
made your method in same class ...
- (void)updateLabel:(NSNotification *)notification{
update label
}
Might be you can try following code -
NSMutableArray *activeControllerArray = [self.navigationController.viewControllers mutableCopy];
for (int i = 0; i< [activeControllerArray count]; i++) {
if ([[activeControllerArray objectAtIndex:i] isKindOfClass:[Reminder Class]) {
Reminder *object = [activeControllerArray objectAtIndex:i];
//Perform all the task here which you want.
break; //Once found break the loop to do further processing.
}
}

Objective-C on iPhone - tab bar crash

I'm relatively new to Objective-C and coding. I've tried doing a little dev on my own but have now become stuck on what is probably a rookie error.
I've created a tab bar controller with 5 views, one such view is a UIWebView. I've got the Webview working and it loads, but when I select a different tab, the app crashes. Please find my code below and any help would be appreciated!
#import <UIKit/UIKit.h>
#interface LiveViewController : UIViewController {
IBOutlet UIWebView *liveView;
}
#property (nonatomic, retain) UIWebView *liveView;
#end
#import "LiveViewController.h"
#implementation LiveViewController
#synthesize liveView;
// The designated initializer. Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
/*
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization.
}
return self;
}
*/
/*
// 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 {
[self.liveView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.britishseapower.co.uk/live/"]]];
[super viewDidLoad];
}
- (void)webViewDidStartLoad:(UIWebView *)liveView
{
// starting the load, show the activity indicator in the status bar
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
}
- (void)webViewDidFinishLoad:(UIWebView *)liveView
{
// finished loading, hide the activity indicator in the status bar
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
- (void)liveView:(UIWebView *)liveView didFailLoadWithError:(NSError *)error
{
// load error, hide the activity indicator in the status bar
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
// report the error inside the webview
NSString* errorString = [NSString stringWithFormat:
#"<html><center><font size=+5 color='red'>An error occurred:<br>%#</font></center></html>",
error.localizedDescription];
[self.liveView loadHTMLString:errorString baseURL:nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
if ( [self.liveView loading] ) {
[self.liveView stopLoading];
}
self.liveView.delegate = nil; // disconnect the delegate as the webview is hidden
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
- (void)dealloc {
[liveView release];
[UIWebView release];
[LiveViewController release];
[super dealloc];
}
#end
Many thanks,
Ryan
[UIWebView release]; [LiveViewController release];
This is what make your app crash.
It's not valid to send a release message to a class itself.
What you've done with [liveView release]; is enough (with the call to [super dealloc];.)
You should also set the delegate to nil in the dealloc method as in the viewWillDisappear method self.liveView.delegate = nil;. This way you're sure to avoid any further message sent to the LiveViewController from the UIWebView.
You should read a bit more of documentation on Objective-C to better understand how it works.
Not sure if this is related but I noticed that you aren't setting yourself as the delegate anywhere in code which means that it must be connected in Interface Builder. Now when the view disappears, you are breaking that connection, but if the view were to re-appear and wasn't previously unloaded that connection will remain broken.
One of the most common reasons why an app may crash is to refer to or send a message to an object that has been already released from the memory. And this type of bug can be easily located using NSZombieEnabled and looking into the console message. So if you haven't already tried that, that's the first thing you must do.
The problem could be in LiveViewController but could be in the other view controllers as well. I wouldn't believe the problem is 100% in LiveViewController because the view controller wouldn't try releasing its view when the view is not shown unless it gets a memory warning. And you run the app using the simulator, it's unlikely it will have a memory warning unless you simulate one.
You would probably know that a view controller never create a view unless the view is used by an object. One of the other view controllers may have a silly bug in its view loading process which causes a crash. Or, you might have released another view controller by mistake. Make 100% sure that the other view controllers have no problem showing their views on their own, when you keep changing between their views (without showing LiveViewController).
So what I would do is to try NSZombieEnabled and check if it accesses a released object, and if it does, what the object is. Also, I will make a double check that the problem is related to LiveViewController. If it doesn't help I would log a message when LiveViewController and its liveView is deallocated (for liveView you need to subclass it). Because delegate property almost always does not retain an object, if the LiveViewController object is released (which shouldn't happen) and liveView still has a reference to it in the delegate property it will make a crash.
Crashes like this are almost always related to releasing an object that has already been released and deallocated.
Let XCode help you find the error. In XCode 4:
- In the toolbar, select the scheme list, and select 'Edit Scheme'
- Select the 'Run Your.app' in the list on the left.
- Under 'Environment Variables', add the following name/value pairs in the appropriate columns:
CFZombieLevel 3
NSZombieEnabled YES
Now when debug your app, you will get a message telling when -release is called on an object that already has a -retainCount of zero. Now you have a good clue to start your investigation.
Note that these flags prevent objects from being deallocated, so it is best to turn them on as needed to prevent out of memory errors.