AdMob in iOS with UIWebView - objective-c

I'm using the latest Xcode (4.4.1) and developing for iOS 5.1. I am utilizing the bottom tab bar interface provided by Apple. One of the tabs uses a UIWebView that utilizes the full screen space. When I try to add a standard banner provided by AdMob, it does not add a banner at all. I was following along with: https://developers.google.com/mobile-ads-sdk/docs/admob/fundamentals. Code attached below
About.h
#import <UIKit/UIKit.h>
#import "GADBannerView.h"
#interface About : UIViewController <UIWebViewDelegate> {
IBOutlet UIWebView *webView;
// Declare one as an instance variable
GADBannerView *bannerView_;
}
#property (nonatomic, retain) UIWebView *webView;
#end
About.m
#import "About.h"
#import "GADBannerView.h"
#import "GADRequest.h"
#import "constants.h"
#implementation About
#synthesize webView;
//#synthesize bannerView = bannerView_;
+ (void)initialize {
// Set user agent (the only problem is that we can't modify the User-Agent later in the program)
NSDictionary *dictionary = [[NSDictionary alloc] initWithObjectsAndKeys:UserAgent, #"UserAgent", nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:dictionary];
}
- (void)viewDidLoad {
[super viewDidLoad];
NSString *fullURL = ([IsBeta isEqualToString: #"true"]) ? #"http://beta.wouldyouratherapp.com/questions/index/0/1" : #"http://wouldyouratherapp.com/questions/index/0/1";
NSURL *url = [NSURL URLWithString:fullURL]; NSURLRequest *requestObj = [NSURLRequest requestWithURL:url]; [webView loadRequest:requestObj];
// Create a view of the standard size at the bottom of the screen.
// Available AdSize constants are explained in GADAdSize.h.
bannerView_ = [[GADBannerView alloc] initWithAdSize:kGADAdSizeBanner];
// Specify the ad's "unit identifier." This is your AdMob Publisher ID.
bannerView_.adUnitID = MyAdUnitID;
// Let the runtime know which UIViewController to restore after taking
// the user wherever the ad goes and add it to the view hierarchy.
bannerView_.rootViewController = self;
[self.view addSubview:bannerView_];
// Initiate a generic request to load it with an ad.
[bannerView_ loadRequest:[GADRequest request]];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
#end
Yes, I have added all the frameworks already, and MyAdUnitID is already defined in the constants file, so everything SHOULD be working, but I guess I am missing something. Any help?

If you're adding the bannerView_, you'll have to decrease the height of your webView accordingly to make room for the bannerView_. Since the origin of the ad looks like its at (0,0), you probably want something similar to this in your adView:DidReceiveAd: callback:
webView.frame = CGRectMake (0, bannerView_.frame.size.height, webView.frame.size.width, webView.frame.size.height - bannerView_.frame.size.height);

Related

WebView being released too many times

I'm trying to load a webpage in a WebView in order to take a snapshot of the website. The WebView is contained in a temporary window that I create for this purpose. However, shortly after I release the WebView and the temporary window, the WebView is sent another release message, while it has already been deallocated. This is the error message in the debugger with NSZombieEnabled set to YES.
*** -[WebView release]: message sent to deallocated instance 0x608000125820
I can't figure out what is causing the WebView to be released too many times. The thing that makes it extra confusing is that the problem only occurs while loading certain URL's. For example: when trying to take a snapshot of http://www.google.com everything is fine, but when using http://edition.cnn.com it almost always crashes.
This is what (a simplified version of) the code looks like:
#interface AppDelegate ()
#property (nonatomic, strong) NSWindow *tempWindow;
#property (nonatomic, strong) WebView *webView;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
CGRect frame = CGRectMake(0, 0, 1200, 695);
self.tempWindow = [[NSWindow alloc] initWithContentRect:frame styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
self.webView = [[WebView alloc] initWithFrame:frame];
self.webView.frameLoadDelegate = self;
self.tempWindow.contentView = self.webView;
[[self.webView mainFrame] loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://edition.cnn.com"]]];
}
#pragma mark - WebFrameLoadDelegate
- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame
{
if (frame != [sender mainFrame]) {
return;
}
// take snapshot here...
[self takeSnapshot];
// get rid of web view and temp window
[self.webView stopLoading:nil];
[self.webView setFrameLoadDelegate:nil];
self.webView = nil;
self.tempWindow = nil;
}
When using ARC there seems to be problem with retaining/releasing the WebView in some situations. From my testings I found out that loading an empty NSStringin the mainFrame of the WebView before releasing it should solve the problem.
See also a short blog entry on this topic.

Launch image not working correctly no matter what

I'm trying to set a launch image to my application, however, it will not work the right way no matter what I do.
My app is landscape only(left or right), but I though I would just put a portrait launch image anyways.
The launch image only shows up if I check the "portrait" box and uncheck the "Landscape left" and "Landscape right" boxes in the "Deployment Info" settings. Obviously I can't do that because it will mess up my whole app.
I tried changing shouldAutorotateToInterfaceOrientation: to return YES in all of my view controllers, but that didn't work.
Any help is appreciated. Thanks!
-Xerif
I was having the same exact probably as your having and unfortunately i have found out that this is a glitch/bug in the Xcode software. I have come up with code that can display a launch image in landscape mode without any size requirements! Try to keep up with me and message me if you have any problems. Here are the steps:
Create a new objective-c file with a subclass of NSObject. Name it GameData.
inside GameData.h enter this code:
#import <Foundation/Foundation.h>
#interface GameData : NSObject
#property (assign, nonatomic) int Mainint;
+(instancetype)sharedGameData;
#end
Now inside of GameData.m enter this code:
#import "GameData.h"
#implementation GameData
+ (instancetype)sharedGameData
{
static id sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
-(void)launchImage
{
[GameData sharedGameData].Mainint = [[NSUserDefaults standardUserDefaults] integerForKey:#"mainint"];
[[NSUserDefaults standardUserDefaults] setInteger:0 forKey:#"mainint"];
}
Now go into your AppDelegate.h file and enter this:
#import "GameData.h"
Now go into your AppDelegate.m file and enter this in the below method:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions
{
[[NSUserDefaults standardUserDefaults] setInteger:0 forKey:#"mainint"]; //Add This
// Override point for customization after application launch.
return YES;
}
Now go into your storyboard and drag a UILabel onto your initial View Controller or the View controller that shows first when app is launched. Don't worry we will hide this label so you will not see it.
Now drag a UIImageView across the entire screen of your initial View Controller
Now go into your initial View Controllers .h file (mine is called ViewController.h) and add the following:
#import <UIKit/UIKit.h>
#import "GameData.h" //Add this
#interface ViewController : UIViewController
#property (weak, nonatomic) IBOutlet UIImageView *launchImage; //Add this
#property IBOutlet UILabel *seconds; //Add this
#property NSTimer *timer; //Add this
#end
Now go into your initial view controller .m file (mine is ViewController.m) and add the following:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize launchImage;
#synthesize timer;
#synthesize seconds;
- (void)viewDidLoad
{
[GameData sharedGameData].Mainint = [[NSUserDefaults standardUserDefaults] integerForKey:#"mainint"];
seconds.hidden = YES;
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(countUp) userInfo:nil repeats:YES];
if ([GameData sharedGameData].Mainint > 3) {
launchImage.hidden = YES;
}
[self countUp];
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)countUp
{
[GameData sharedGameData].Mainint += 1;
seconds.text = [NSString stringWithFormat:#"%i", [GameData sharedGameData].Mainint];
if ([GameData sharedGameData].Mainint == 3) {
launchImage.hidden = YES;
[GameData sharedGameData].Mainint = 4;
[[NSUserDefaults standardUserDefaults] setInteger:[GameData sharedGameData].Mainint forKey:#"mainint"];
}
}
If you follow those steps exactly it should work and you should have no problems. If something does not work just let me know i'd be happy to help. I searched for days and days trying to find the answer and then finally figured it out through testing this code. Good Luck!

Resizing a WebView instance

I'm just starting to learn OSX Cocoa app development. I would like to display a website inside a native OSX window. I thought a WebView would be the right way. I would like the webview to always take up 100% of the containing windows' size.
After struggling a bit, I understand how to catch the 'window resize' event, but I have no clue how to resize the web view according to the windows new size.
Here's what I have so far:
AppDelegate.h
#import <Cocoa/Cocoa.h>
#import <WebKit/WebKit.h>
#interface AppDelegate : NSObject <NSApplicationDelegate, NSWindowDelegate>
#property (assign) IBOutlet NSWindow *window;
#property (weak) IBOutlet WebView *websiteWebview;
#end
AppDelegate.m
#import "AppDelegate.h"
#implementation AppDelegate
- (NSSize) windowWillResize:(NSWindow *)sender toSize:(NSSize)frameSize
{
WebView *view = [self websiteWebview];
[view setFrame:CGRectMake(0, 0, 1000, 1000)];
return frameSize;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[[self window] setDelegate:self];
NSURL *url = [[NSURL alloc] initWithString:#"http://conradk.com"];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
[[[self websiteWebview] mainFrame] loadRequest:request];
}
#end
I thought calling [view setFrame:CGRectMake(0, 0, 1000, 1000)] would resize the web view as well, but it seems to not be the case.
Any tips / hints please? Is a WebView the right way to do this? Thanks for your help!
You need to make your WebView part of the window's contentView.
[self.window setContentView:self.websiteWebview];
By default, this will let the webView auto-resize with the window. You'll only need to mess with the sizing if you want the webview to do something other than match the size of the window.

add GLKViewController to subview - GLKView context causes crash

I have a pretty simple set up in mind, having a mainViewController that has a GLKViewController on top of it. The idea is having my GLKViewController in a box, that take sup 1/3 of the screen, on the mainViewController. This can be seen below:
That white box is my own custom GLKViewController with the follow code:
boxViewController.h
//boxViewController.h
#import <UIKit/UIKit.h>
#import <GLKit/GLKit.h>
#interface boxViewController : GLKViewController
#end
boxViewController.m
//boxViewController.m
#import "boxViewController.m"
#interface boxViewController () { }
#property (strong, nonatomic) EAGLContext *context;
#end
#implementation boxViewController
-(void)viewDidLoad {
[super viewDidLoad];
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (!self.context) {
NSLog(#"Failed to create ES context");
}
GLKView *view = (GLKView *)self.view;
// view.context = self.context;
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
}
#end
On my mainViewController in the viewDidLoad I simply call boxViewController like this:
boxViewController* box = [[boxChartViewController alloc] init];
box.view.layer.frame = CGRectMake(10, 50, self.view.frame.size.width-20, self.view.frame.size.height/3);
[self.view addSubview:box.view];
which works perfect.
Notice that in my boxViewController.m I had view.context = self.context commented out. If you uncomment it, my application crashes without any error messaging (it breaks with a EXC_BAD_ACCESS in the objc_msgSend assembly code [line 8 to be specific]).
What am I doing incorrectly that when I set the context the application crashes? From all the tutorials I noticed that they have the same set up, except not setting the controller on another controller. Though I don't understand why GLKViewController couldn't be framed on another controller, so I don't think that's the issue.
After a few hours of messing around I found that adding the viewController as a child works:
#import "mainViewController.h"
#implementation mainViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.layer.backgroundColor = [UIColor colorWithRed:242.0f/255.0f green:242.0f/255.0f blue:242.0f/255.0f alpha:1.0].CGColor;
boxViewController* chart = [[boxViewController alloc] init];
chart.view.layer.frame = CGRectMake(10, 50, self.view.frame.size.width-20, self.view.frame.size.height/3);
chart.view.layer.borderColor = [UIColor blackColor].CGColor;
chart.view.layer.borderWidth = 2.0f;
[self addChildViewController:chart];
[self.view addSubview:chart.view];
}

OO approach to view controller classes in obj-c

I have 10 different pages built into a slider control in my app, these pages are setup using storyboards.
The only difference on each page is a different web view to display rich text and a different image as a background are used.
Is it possible for me to have one view controller for all 10 pages and setup some flags in the constructor which would be executed on every page when its loaded to tell it what image and web view to show? If so what would this look like?
Thanks,
Lewis.
You can create subclass of UIViewController, for example MyViewController.
And then replace your .h file with:
#import <UIKit/UIKit.h>
#interface MyViewController : UIViewController
#property (nonatomic, strong) UIWebView *myWebView;
#property (nonatomic, strong) UIImageView *myImageView;
- (id)initWithURLString:(NSString *)urlString image:(NSString *)imageName;
#end
And your .m file with:
#import "MyViewController.h"
#interface MyViewController ()
#end
#implementation MyViewController
#synthesize myWebView = _myWebView;
#synthesize myImageView = _myImageView;
- (id)initWithURLString:(NSString *)urlString image:(NSString *)imageName
{
self = [super init];
if (self) {
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
_myWebView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 400)];
[_myWebView loadRequest:urlRequest];
_myImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:imageName]];
_myImageView.frame = CGRectMake(0, 400, 60, 60);
}
return self;
}
#end
And then just create an instances of your UIViewController subclass:
MyViewController *vCon = [[MyViewController alloc] initWithURLString:someURLString image:imageName];
I think the best approach would be to define a data source for your view controller. This data source would have the following interface, e.g.:
#protocol MyVCDataSource
- (NSUInteger)numberOfPages;
- (NSString*)htmlContentForPageIndex:(NSUInteger)index;
- (NSString*)backgroundForPageIndex:(NSUInteger)index;
#end
You would provide your view controller with a member called datasource that would be initialized in the initWithDatasource method:
#interface MyVC : UIViewController
...
#property (nonatomic, weak) id<MyVCDataSource> datasource;
- (id)initWithDatasource:(id<MyVCDataSource>)ds;
Then your view controller would just ask the data source for the HTML data or the background file name when it needs it:
- (void)viewDidLoad {
self.view.backgroundColor = GET_BACKGROUND_FROM_STRING([self.datasource backgroundForPageIndex:self.currentIndex]);
[self.webView loadHTMLString: [self.datasource htmlContentForPageIndex:self.currentIndex]];
....
}
I assumed that the protocol just returns strings, but indeed you can have it return what you need (e.g., an image, a color, an URL), it all depends on the internals of your class.
Finally, your datasource object could be any object (even your MyVC instance) and return its data by indexing into an array:
- (NSString*)htmlContentForPageIndex:(NSUInteger)index {
return [self.htmlPages objectAtIndex:index];
}
etc.