Thứ Năm, 5 tháng 8, 2010

How To Integrate iAd into Your iPhone App


1) Integrate iAd 2) ??? 3) PROFIT!

1) Integrate iAd 2) ??? 3) PROFIT!

With the iOs SDK 4 now public and the advent of iAds just a few days away, I thought we’d celebrate with a tutorial on how to integrate iAd into your iPhone app!

In this tutorial, not only will we show you how to get started with iAd, but we’ll also show you how to deal with some complex issues you may run into along the way such as:

  • Supporting both Portrait and Landscape ads in the same app
  • Integrating into a Universal app
  • Maintaining backwards compatibility with iOs 3.0
  • What to do if you are using a UITableViewController!

We’re actually going to start with where we left off in the How To Port an iPhone Application to the iPad and use the universal app we developed in that tutorial in the starting point.

So grab a copy if you haven’t already, and let’s get to adding some iAds!

Base SDK vs. Deployment Target

The first step to use iAd is to make sure our project has the right Base SDK and iPhone OS Deployment Target selected.

For those of you confused about the difference between the Base SDK and Deployment Target (like I was for quite some time!), here’s what they mean:

  • The Base SDK is the version of the SDK you are linking against. Your app can use any classes or functions available in the version of the SDK you choose here – as long as they are available on the actual device the code runs on.
  • The Deployment Target is the earliest possible version of the SDK your code can run on. This can be an earlier version than the Base SDK – in fact you often want to set it to be earlier to ensure that as many different versions of the OS can run your code as possible!

The tricky bit is what happens when you want to use a class, function, or framework available in one version of the OS if it’s available, but still work on the old version of the OS if it isn’t. We already did some of this in How To Port an iPhone Application to the iPad, and we’ll do even more in this tutorial!

For this tutorial, we want to set things up so that our code can use stuff available in iOS 4.0 (such as iAd), but still run on as many devices as reasonable (3.0+).

So first let’s set iOs 4.0 as the base SDK. To do this, expand the Targets directory, right click on PortMe, and choose “Get Info”. Click the Build tab, make sure “All Configurations” is selected, navigate to Architectures\Base SDK, and change the value to iPhone Device 4.0.

Screenshot of Setting Base SDK

Then, let’s set iPhone OS 3.0 as the iPhone OS Deployment Target. To do this, still in the Target Build tab, navigate to Deployment\iPhone OS Deployment Target, and change the value to iPhone OS 3.0.

Screenshot of setting Deployment Target

You should now be able to compile and run your app (use the iPhone simulator), and try it out on an iPhone 4 simulator. Once you run your code, in the simulator choose Hardware\Device\iPhone OS 4 and re-run your app. The simulator window will look a little different, and say iPhone 4 in the toolbar, so you’ll know it’s working!

Screenshot of PortMe on iOS4

Linking Against the iAd Framework

The next thing we need to do is add the iAd framework to the project. You can do this by right clicking on Frameworks, choosing “Add\Existing Frameworks…”, and choosing “iAd.framework”.

The problem is, if that is all we do our code will break on older devices that don’t have the iAd framework.

You can verify this by trying to run your code in the iPad Simulator 3.2 – boom! The app will crash on startup and you’ll see the following error log:

dyld: Library not loaded: /System/Library/Frameworks/iAd.framework/iAd
Referenced from: /Users/rwenderlich/Library/Application Support/
iPhone Simulator/3.2/Applications/
3ACB1BDA-26F6-43A6-84EA-9FB637B8CDCD/PortMe.app/PortMe
Reason: image not found

To fix this, we need to weak link against the iAd framework. Expand the Targets directory, right click on PortMe, and choose “Get Info”. Click the Build tab, make sure “All Configurations” is selected, and navigate to Linking\Other Linker Flags. Double click on that entry, click the “+” button, and type “-weak_framework iAd”.

Click OK, and then try your app on the iPad simulator again and viola – it should work!

Preparing our XIB

In this tutorial, we’re going to integrate iAd into both the PortMeGameListController and the PortMeGameDetailsController. However, the integration is a bit easier in the PortMeGameDetailsController because it is a subclass of UIViewController, so we’re going to start there first.

Open up PortMeGameDetailsController.xib. You’ll see that all of the controls are children of a single view:

Details View Controller Settings - Before

What we’re going to need to do with iAd is scroll an ad view onto the screen when an ad is available, and shrink the rest of the content to fill the remaining space. As currently designed, this isn’t that easy because all of the controls are direct children of the root view. But there’s an easy way to fix it – we’ll simply move the controls into a subview instead!

The easiest way to do this is to drag another view from the library into the XIB, and change its size to be the same as the existing view’s size (320×416). Then drag the existing view as a subview of the new view. When you’re done, it should look like the following:

Details View Controller Settings - After

Then, control-drag from the File’s Owner to the new view (which is now the root view) to connect it to the view outlet. Save your XIB, and run the project and verify that everything still works OK with the details view (in particularly that orientation resizing works correctly). If all works well, we’re one step closer to integrating iAd!

Simple iAd Integration

Ok, now let’s get to the fun part – integrating iAd!

First, make the following changes to PortMeGameDetailsController:

// In the import section
#import "iAd/ADBannerView.h"

// Modify the PortMeGameDetailsController interface
@interface PortMeGameDetailsController : UIViewController
{

// Inside the PortMeGameDetailsController interface
UIView *_contentView;
id _adBannerView;
BOOL _adBannerViewIsVisible;

// After the interface
@property (nonatomic, retain) IBOutlet UIView *contentView;
@property (nonatomic, retain) id adBannerView;
@property (nonatomic) BOOL adBannerViewIsVisible;

We first include the iAd headers and mark the view controller as implementing the ADBannerViewDelegate. This way, we can receive events as ads become available or not.

We then declare a property to keep track of the content view that contains all of the controls (basically the inner UIView). We also declare a variable to keep track of our iAd banner view, and whether or not it’s currently visible.

Note that we declare the iAd banner view as an id variable rather than as a ADBannerView. This is because we want to ensure backwards compatibility all the way to OS 3.0, and the ADBannerView class is only available on 4.0+, so we need to weak link against it.

Before we forget, let’s hook up our content view to the new outlet we just made. Make sure you save PortMeGameDetailsController.h, go back to PortMeGameDetailsController.xib, control-drag from the File’s Owner to the inner (second) UIView, and connect it to the contentView outlet.

Then switch over to PortMeGameDetailsController.m and make the following changes:

// In the synthesize section
@synthesize contentView = _contentView;
@synthesize adBannerView = _adBannerView;
@synthesize adBannerViewIsVisible = _adBannerViewIsVisible;

// In the dealloc section
self.contentView = nil;
self.adBannerView = nil;

Next, we’re going to add the meat of the code. But there’s a lot of it – so let’s break it down into 6 steps.

1) Add helper functions to get height of iAd banner

- (int)getBannerHeight:(UIDeviceOrientation)orientation {
if (UIInterfaceOrientationIsLandscape(orientation)) {
return 32;
} else {
return 50;
}
}

- (int)getBannerHeight {
return [self getBannerHeight:[UIDevice currentDevice].orientation];
}

There are several places in the rest of the code where we’re going to want to know how large the banner view should be given a particular orientation. Currently iAds have two possible sizes: 320×50 for landscape, or 480×32 for portrait. So we simply retrieve the proper height based on the passed in orientation.

2) Add helper function to create the iAd view

- (void)createAdBannerView {
Class classAdBannerView = NSClassFromString(@"ADBannerView");
if (classAdBannerView != nil) {
self.adBannerView = [[[classAdBannerView alloc]
initWithFrame:CGRectZero] autorelease];
[_adBannerView setRequiredContentSizeIdentifiers:[NSSet setWithObjects:
ADBannerContentSizeIdentifier320x50,
ADBannerContentSizeIdentifier480x32, nil]];
if (UIInterfaceOrientationIsLandscape([UIDevice currentDevice].orientation)) {
[_adBannerView setCurrentContentSizeIdentifier:
ADBannerContentSizeIdentifier480x32];
} else {
[_adBannerView setCurrentContentSizeIdentifier:
ADBannerContentSizeIdentifier320x50];
}
[_adBannerView setFrame:CGRectOffset([_adBannerView frame], 0,
-[self getBannerHeight])];
[_adBannerView setDelegate:self];

[self.view addSubview:_adBannerView];
}
}

This helper function creates an ADBannerView in a manner that is safe to use across multiple OS versions. It uses weak linking and NSClassFromString to check if the ADBannerView class is available – if it is not, the method will return nil and the function will bail.

However, if it is available it creates an instance of the class. It then uses the setRequiredContentSizeIdentifiers to specify what kind of ads this app needs. For our case, our app supports both portrait and landscape modes so it needs both ad options.

It then calls setCurrentContentSizeIdentifier to tell iAd which ad it should display. We simply choose the correct one by looking at the current orientation.

Next, we need to set the frame for the iAd. Note there’s some funky business here – we actually set the frame of the view to be offscreen! This is because we don’t know if an ad is available yet, and we don’t want to display the view until we know one is.

We set our view controller as the delegate so that we can receive notice about iAds being available or not. Then finally we ad the new iAd banner view as a subview of our view!

Note something subtle about the above – we always use message passing syntax rather than dot notation (i.e. [_adBannerView setRequiredContentSizeIdentifiers:...] instead of _adBannerView.requiredContentSizeIdentifiers = …). This is again to make sure everything runs fine on OS 3.0+.

3) Add function to size views correctly

- (void)fixupAdView:(UIInterfaceOrientation)toInterfaceOrientation {
if (_adBannerView != nil) {
if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation)) {
[_adBannerView setCurrentContentSizeIdentifier:
ADBannerContentSizeIdentifier480x32];
} else {
[_adBannerView setCurrentContentSizeIdentifier:
ADBannerContentSizeIdentifier320x50];
}
[UIView beginAnimations:@"fixupViews" context:nil];
if (_adBannerViewIsVisible) {
CGRect adBannerViewFrame = [_adBannerView frame];
adBannerViewFrame.origin.x = 0;
adBannerViewFrame.origin.y = 0;
[_adBannerView setFrame:adBannerViewFrame];
CGRect contentViewFrame = _contentView.frame;
contentViewFrame.origin.y =
[self getBannerHeight:toInterfaceOrientation];
contentViewFrame.size.height = self.view.frame.size.height -
[self getBannerHeight:toInterfaceOrientation];
_contentView.frame = contentViewFrame;
} else {
CGRect adBannerViewFrame = [_adBannerView frame];
adBannerViewFrame.origin.x = 0;
adBannerViewFrame.origin.y =
-[self getBannerHeight:toInterfaceOrientation];
[_adBannerView setFrame:adBannerViewFrame];
CGRect contentViewFrame = _contentView.frame;
contentViewFrame.origin.y = 0;
contentViewFrame.size.height = self.view.frame.size.height;
_contentView.frame = contentViewFrame;
}
[UIView commitAnimations];
}
}

This is a helper function we can call to make sure our views are in the right position. If ads are available, we want the ad banner view to be at the top of the screen and the content view shrunk a bit to fill the rest of the area. If ads are not available, we want the ad banner view offscreen and the content view as large as the entire view here.

And that’s exactly what the above function does. It looks long, but is fairly simple and self-explanatory. Note that we wrap the resizing code in an animation block to make things look awesome.

4) Call createAdView in viewDidLoad

- (void)viewDidLoad {
[self createAdBannerView];
}

We want to create our ad view as soon as our view is loaded, even if we aren’t ready to display it quite yet.

5) Call fixupAdView in viewWillAppear and willRotateToInterfaceOrientation

- (void) viewWillAppear:(BOOL)animated {
[self refresh];
[self fixupAdView:[UIDevice currentDevice].orientation];
}

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[self fixupAdView:toInterfaceOrientation];
}

We need to fix up our ad view in viewWillAppear, because the device may have changed orientations in the time between when our view was visible last and now. And we obviously need to change it upon rotation as well!

6) Implement ADBannerViewDelegate

#pragma mark ADBannerViewDelegate

- (void)bannerViewDidLoadAd:(ADBannerView *)banner {
if (!_adBannerViewIsVisible) {
_adBannerViewIsVisible = YES;
[self fixupAdView:[UIDevice currentDevice].orientation];
}
}

- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
if (_adBannerViewIsVisible)
{
_adBannerViewIsVisible = NO;
[self fixupAdView:[UIDevice currentDevice].orientation];
}
}

Now that we have our helper functions, implementing the ADBannerViewDelegate methods are quite simple. We simply toggle whether the ad banner view should be visible or not, and call fixupAdView.

Done!

And that’s it! Compile and run your project, and you should see ads appear correctly in both portrait and landscape mode.

iAd in List View - Portrait Mode

iAd in List View - Landscape Mode

And best yet – if you run the code on an iPad or iPhone 3.0 device it will work just fine as well, but without ads!

UITableView integration

Well that worked great for our details controller, but we want it in our list controller too!

The problem is our list controller is a UITableViewController. Unfortunately, it seems like the best way to deal with this situation is to convert your UITableViewController to a normal UIViewController and then proceed similarly to the way we did above. So here are all of the gory steps:

1) Create a XIB for PortMeGameListController

Go to File\New File, choose User Interface and View XIB, make sure Product is iPhone, and click Next. Name the XIB PortMeGameListController.xib and click Finish.

Open up the XIB, click on the File’s Owner, and in the fourth tab of the Attributes Inspector change the class to PortMeGameListController.

Then drag a UIView into the current UIView (so there are 2, just like we did before), and add a UITableView to the inner view. When you’re done it should look like this:

XIB settings for PortMe's List View Controller

2) Make some changes to PortMeGameListController

Inside PortMeGameListController.h:

// Change the interface declaration
@interface PortMeGameListController : UIViewController {

// Add inside class
UITableView *_tableView;
UIView *_contentView;

// Add after class
@property (nonatomic, retain) IBOutlet UITableView *tableView;
@property (nonatomic, retain) IBOutlet UIView *contentView;

Inside PortMeGameListController.m:

// In synthesize section
@synthesize tableView = _tableView;
@synthesize contentView = _contentView;

// In dealloc section
self.tableView = nil;
self.contentView = nil;

Don’t forget to save the files!

3) Hook up outlets

Now go back to PortMeGameListController.xib and connect the first view to the view outlet, the second to the contentView outlet, and the third to the tableView outlet.

Also control-drag from the tableView back to the File’s Owner and set it as the delegate and datasource.

4) Set the NIB name for PortMeGameListController in MainWindow

Open MainWindow.xib and MainWindow-iPad.xib, expand the Navigation Controller, select “Port Me Game List Controller”, and change the nib name to PortMeGameListController.

5) Compile and test to make sure everything works as usual

At this point, compile and run your code and make sure everything works as it usually does – but now you’re using a UIViewController rather than a TableViewController, and you have a XIB laid out in a nice way to use iAds!

6) Follow the steps from the previous section

Now you’re exactly where we were in the previous section with a view controller – so follow the same steps to integrate in this view!

Done!

If all goes well, you should be able to compile and run your project and see advertisements at the top of your table view!

iAd in Table View - Portrait Mode

iAd in Table View - Landscape Mode

Where To Go Now?

Here is a sample project with all of the code we’ve developed in the above tutorial.

Now you should know how to integrate iAds into your projects – no matter what OSs you wish to support for your app! I’d love to hear your experiences with iAds and how well they are working (or not) for your app!

Category: iPhone

Tags: , ,





88 Comments

  1. Marin Todorov (9 comments) says:

    Ray you are a pure inspiration :) If I could only quit my day job I’d be following in your footsteps to share iPhone knowledge to everyone in such a cool way

    Best, wishes

  2. Shawn Grimes (1 comments) says:

    Awesome tutorial Ray. Thanks so much for the info, I’m looking forward to giving this a try. I really like that you took iOS 3.0 into consideration with this and provided code that also provided backwards compatibility components.

  3. Abhinav (2 comments) says:

    Hey.. Very useful.. I was just wondering how to start and u finished it up… Thank You.

    When i run it on my iPad, I cant see the advertisement. Why is that so?

  4. Ray Wenderlich (399 comments) says:

    @Marin, @Shawn: Thanks guys for the kind words! :]

    @Abhinav: iAds are only available on iOS4+, so they will not work on iPad yet!

  5. Abhinav (2 comments) says:

    Ok. So, When it will be available on iPad..??
    Can I upgrade iPad to 4.0?

  6. olinsha (11 comments) says:

    Hi Ray,
    I have finally decided to add a comment to one of your tutos just to let you know that your site has become my number one information source for iPhone dev.
    Your tutos are clear and very helpfull.
    So keep going and share your great knowledge with us to build a big community that will make the development skillz for iPhone / iPad evolving !

    best regards from France ;)

  7. Ray Wenderlich (399 comments) says:

    @Abhinav: Not sure when iOS4.0 will be available for iPad, but for now – unfortunately no iAds on iPad!

    @olinsha: Wow, thanks for the kind words! I think we have an awesome and helpful community of iPhone developers so trying to do my part!

  8. Alon Ezer (2 comments) says:

    Hey Ray,
    Thanks for the tutorial.

    Could you explain how to implement it in cocos2d?

    Thanks!!

  9. Vlad Alexa (1 comments) says:

    Declaring the ADBannerView as id is totally unnecessary since you already weak linked the framework, which can be done more easily by changing the type to weak in the general tab of your target’s settings

  10. Asad (6 comments) says:

    Hello Ray

    i want to add a button and when a user tape the button , the player should move, but i want to add 4 sided button like when the user drag the button on right side the player should move on the right side and when the user drag the button on left side the player should move onto the left side.i want to use one button instead of 4 buttons and want 4 functionality from the one button rather than using 4 buttons because iphone screen is limited.
    please reply me, i need your help.

    thanks Ray

  11. Ray Wenderlich (399 comments) says:

    @Alon Ezer: Maybe in a future tutorial, we’ll see! But to get you started, you should be able to just put the Cocos2D view inside a view controller, and then follow the rest of the tutorial as-is.

    @Vlad: Good points on both cases, didn’t realize that, thanks for the tips!

    @Asad: I think you are posting in the wrong thread, this is a thread about integrating iAd ;]

    However, it sounds like you are looking for a joystick implementation in Cocos2D. There are a bunch of guys that have released code for this, here’s a link to one that looks good:

    http://github.com/sneakyness/SneakyInput

  12. Thang Tran (15 comments) says:

    Hi Ray,

    thank you very much for this helpful tutorial. That’s exactly what I’m looking for to solve my problem.

    I did it so well with your tutorial’s supports.

    However, I have one question. I’ve got so many “warning”… it’s about “invalid receiver type ‘id*’”

    Of course the application is running well. However, I don’t know if those warning make my app got rejected on the AppStore. Do you have any ideas?

    Best Regards,
    Thang

  13. Ray Wenderlich (399 comments) says:

    @Thang: Hm, not sure I just re-built the sample project and don’t get any warnings. Do you see warnings compiling the sample project?

    One thing to check is that you declared your id variables like “id foo”, not “id * foo”.

  14. Thang Tran (15 comments) says:

    Hi Ray,

    thank you very much. I’ve solved the problem. I didn’t notice that I should declare the id adView instead of id *adView.

    However, I’ve been getting some warnings even in the auto-gererated code from XCode. Like when I use UITableView, I got a warning inside
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    I’m gonna submit my first app on AppStore. I’m wondering if the warning fail my app submission or not. The best way should be trying ;) I will try and let you know if I got a reject because of the warning.

  15. Dion (1 comments) says:

    thanks for the tutorial. i just have one question. is that it? once a app is submitted, if you the ads will simply appear instead of that Test advertisement message??

  16. Thang Tran (15 comments) says:

    @Dion: YES

    But in order to have it, you must complete a special contract on iTunes Connect (like you did for selling apps) to have the ability to get the ads’ contents.

  17. Ray Wenderlich (399 comments) says:

    @Thang: Apple won’t reject you if your code has warnings (I doubt they will even know such things), however you should be careful about warnings because quite often they indicate bugs in your code that you should address. A lot of people like compiling their code with an option that sets warnings to errors just so they are forced to go through and clean up warnings.

    Thanks for answering Dion’s question too btw!

  18. Johng1492 (1 comments) says:

    Ray,

    Nice article but…

    I downloaded your code and ran it without change and the iAd test ads do not come up. Is there something on my end that I need to set in order to get your app functioning?

    Good work.

    John

  19. Marin Todorov (9 comments) says:

    Hi Ray,

    I know I already left a comment, but I’m banging my head against the wall here, so I thought you might give me a tip …

    When I installed the latest XCode I went straight to try your iAd example – and it works great in the iPhone 4 Simulator and the iPad Simulator, but I want to actually be sure it works on 3.1 and I see the 3.1 images are there in the Simulators folder, but XCode does not list them anymore … What do you say, did you find way to test on 3.1 Simulator somehow ?

    Thanks a lot, hope you have the right tip as always,
    Marin

  20. Thang Tran (15 comments) says:

    @Ray: thanks for your comment
    @Marin: I’m afraid that you can’t test on the 3.1 simulator. But you can test on the 3.1+ devices. My iPhone is running 3.1.3 and I can test the app when I plug my iPhone with XCode, and I selected Device 4.0

  21. Ray Wenderlich (399 comments) says:

    @John: Just downloaded the sample code and tried it out, and I see ads on my end. So not sure! Could be maybe the test ad server was down, or some kind of internet connectivity issue?

    @Thang: Thanks again for helping out! Yeah that’s what I did back when my device was running 3.1.3 too… however now that it’s running 4.0 not sure what I’ll do for testing here on out!

  22. Thang Tran (15 comments) says:

    @Ray: it has been being a big discussion on devforum.apple.com

  23. olinsha (11 comments) says:

    Hello Ray,
    thanks to your tuto I switched one of my app from AdMob (totally screwed with iOS4) to iAd. On my iPhone using iOS 4 and using the latest X-Code it’s working fine
    But I also kept an older X-Code version to test my apps on older OS. And when I build my app for OS 3.1.3 I have plenty of errors (42 !!) that are all related to iAd :
    * iAd/ADBannerView.h: No such file or directory
    * cannot find protocol declaration for ‘ADBannerViewDelegate’
    and so on.
    I don’t understand the issue as I made iAd framework dependance weak. So I am not sure that we can really make such application run on older OS.

  24. Asad (6 comments) says:

    hi Ray

    i have integrated iAd in my app with the help of your great tutorial but i don’t know whether it shows only one ad or there is a log in account or a secret key (like in case of admob, mobclix) to show ads changing after some instant of time.

    Thanks in advance….

  25. Eric (15 comments) says:

    This is really nice. We were trying to get it to do the same thing, but have the ad on the bottom, and the image move up. I see how to move where the ad is, but the image is moving always up. I am trying to adjust some of the logic but so far no luck.

    Any suggestions? Thanks!

  26. Ray Wenderlich (399 comments) says:

    @olinsha: An app with iAd will not compile with an old version of the SDK because it requires symbols in the iOS4+ SDK. However, the app itself should run just fine on devices running iOS 3.0+ if you build it with the above instructions. The best way to test is on a device running iOS3.0 AFAIK.

    @Asad: You don’t need to integrate a secret key or anything like some other APIs.

    @Eric: What do you mean the image is always moving up? I assume you already modified the code in fixupAdView to put the ad on the bottom and the content view a bit smaller but on top?

  27. John (3 comments) says:

    Ray,

    Have you found problems when you have a view that is nested below Nav controllers, tab views and in a table?

    I get your app working (and the WWDC app sample) working no problem. But when I try to include it in my working app with tab views/nav controllers and a table view, the apps don’t want to show.

    Thoughts?

    Great work.

    John

  28. Marin Todorov (9 comments) says:

    Other people tell that installing the old XCode on an external drive will let you use the 3.1.3 Simulator, didn’t try it myself, but probably in the weekend I’ll have time to give it a try!

  29. Bob Schoenburg (6 comments) says:

    I “grafted” the code into one of my existing apps, and it works! One problem: sometimes I get compiler complaints about mycontroller may not respond to fixupAdView????

    Thanks for a great tutorial

  30. samson (11 comments) says:

    First, thanks a lot for the great article.

    Question: I followed the article and can see the “test ad” in simulator. But when I load the app onto my test device (iPhone 3GS with OS 4.0), the ad bar is always blank, doesn’t show the test message. And console shows this:

    Error Domain=ADErrorDomain Code=0 “Invalid app id/account information” UserInfo=0×26eac0 {NSLocalizedDescription=Invalid app id/account information}

    Did I do anything wrong or is it expected behavior? (e.g., ad won’t show up on device until approval?)

    I already submitted the app for review anyways. During uploading, I made sure I did turn on iAd, and completed the contract process.

    Thanks!

  31. Ray Wenderlich (399 comments) says:

    @John: I’ve tried it in several of my apps that have nav controllers without any issues – haven’t tried it with tab bar controllers but I can’t see why that shouldn’t work. Have you tried debugging through and seeing if the resize code gets called and printing out the frames of the views, etc?

    @Marin: You’re right about that, in fact Matt Gallagher just came out with a nice post that mentions this as well as some other useful tips:

    http://cocoawithlove.com/2010/07/tips-tricks-for-conditional-ios3-ios32.html

    @Bob: To avoid those warnings, make sure fixupAdView is declared before you call it, or add the signature of fixupAdView to your class’s header.

    @samson: Hm, I haven’t seen that problem. Maybe it was due to timing – when you create the app in iTunes Connect, you have a checkbox to enable the app for iAd support. Let me know if it’s working for you when it gets approved.

  32. Bob Schoenburg (6 comments) says:

    Hi Ray,

    The problem indeed got fixed when I declared the items in the .h files as needed.

    Thanks

  33. Samson (11 comments) says:

    Ray, thanks for the reply.

    I double checked my app on ITC. It is waiting for review and the info does say “iAd is enabled”. So I guess it should be fine. I will keep you updated once it’s approved (or rejected for reasons related to iAd).

    Btw, I forgot to mention in my previous message: that error message was actually printed from adbannerviewdelegate’s didFailToReceiveAd callback.

  34. ltgbau (2 comments) says:

    Hi Ray!
    Thanks for the tut, I’ve just intergrated iAds to my app. I’ll test and submit it soon.
    Thanks again!

  35. Tony (2 comments) says:

    downloaded your code, ran in and rotate (iad) work perfectly , while in landscape i click in test advert and get this in debugger

    The view controller returned NO from -shouldAutorotateToInterfaceOrientation: for all interface orientations. It should support at least one orientation.

    Normal ?

    Great Tutorial :)

    T

  36. Joseph Nardone (3 comments) says:

    Great article.

    I am having the same problem as Sampson. I have successfully integrated iAds into several new apps but when I activated iAds for an old app and then added my functional iAd code to the debug version, I get the same error message as Sampson on the device.

    I can’t run on 4.0 simulator because I have Quattro SDK installed for back-up ads.

    Thanks,
    Joseph

  37. Forrest (5 comments) says:

    Anyone had tried to integrate iAds into three20 ? To be more specific, how about TTPhotoView ?

  38. Ray Wenderlich (399 comments) says:

    @Samson/@Joseph: Yeah I get that shouldAutorotateToInterfaceOrientation error too. Maybe a bug with the test ads…? Either way, doesn’t seem to cause a problem in my shipped apps with iAds.

    @Forrest: I have not, but I’d imagine it would involve a lot of digging around Three20 code unfortunately :P Maybe try the Three20 forums on this one, they might have some good tips to get you started on that.

  39. Joseph Nardone (3 comments) says:

    Thanks Ray but that is not the error we were talking about. We got an “Invalid app id/account information” when running on the device.

    Mine went away and turned into a “no qualified ads” error so I still get no test ads with working code on my device.

    Joseph

  40. @gojia (1 comments) says:

    hi, ray.
    I’m always thanks for your good tutorial.

    I’m trying this IAD tutorial.
    but I have some problem.

    4.0, 3.1.3 Simulator no problem!
    4.0 Device no problem.
    but 3.1.3 Device have error. like this.

    warning: Unable to read symbols from “iAd” (not yet mapped into memory).
    Error launching remote program: security policy error
    The program being debugged is not being run.

    What should I have to??
    Thank you for your time.

  41. samson (11 comments) says:

    @ray, the issue Joseph and I met should have nothing to do with auto-rotation. It’s that the ad is always blank and the delegate’s didFailed… is called with “invalid app id/account” error. Also the following seems to be true for both of us:

    - Only occurs on device. On simulator it does show “test ad”. (Seems Joseph can’t run simulator for some reason but he does see the same problem on device)

    - Occurs only when you try to upgrade an old app with iAd. (I haven’t tried any new apps but Joseph said new apps are fine)

    @tony: the auto rotation error you saw is more like a warning. To avoid it you just need to do this:

    - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    return interfaceOrientation == UIInterfaceOrientationPortrait;
    }

    This tells the OS your app only supports portrait mode and can’t rotate at all.

    Somehow the template code doesn’t write this part for you. Apparently it still works the same, but the OS is just reminding you to be more specific.

  42. Asad (6 comments) says:

    Hello RaY

    I have implemented iAd in my application but it only shows Test Advertisement on ads. i have agreed to iAd agreement also. My question is that whether this ad is changing or its statically only one ad, i also want to upload my app to appstore but it contains only one ad. i think if my app gets ready for sale, it will change ads automatically.
    please give me some help in this regards.

    Thanks

  43. Thang Tran (15 comments) says:

    @Asad: you Ad will be fully controlled by Apple iAd server. So you don’t need to care about changing you ad. The only task you have to do after signing the iAd contract is integrate ADBannerView into your apps.

    The actual ads will appear when people download your app from AppStore.

  44. Asad (6 comments) says:

    Special thanks to Thang Tran, i am going to upload my app to appstore.

  45. Thang Tran (15 comments) says:

    @Asad: you are welcome :)

    Congratulation, you are going to have a new app your the store, and going to make money. Just share the link when it is available on the store. Best wishes.

  46. Bob Schoenburg (6 comments) says:

    Uploaded my app, waiting for review. Thanks to all for the help and comments.

  47. samson (11 comments) says:

    Hi All,

    I just found out something: after you login ITC, there is an item called “iAd networks”. From here you can check your app’s iad state. For example mine shows:

    Testing: This app is receiving test ads. This app’s status is not yet Ready For Sale. You have not configured ad preferences for this app.

    Well this still doesn’t explain why it shows blank id and invalid id error on device, but hope it’s helpful for others.

  48. MarkAtAR (3 comments) says:

    I seem to be getting EXC_BAD_ACCESS crashes in the simulator after adapting the code (without rotating) into my app. Has anyone else experienced this, or does anyone know where I could have gone wrong?

    I have the correct code in the dealloc section, that was my first double check.

  49. Ray Wenderlich (399 comments) says:

    @Josesph/@samson: OK, I did some more testing on this… I ran both one of my apps with shipped ads, and the sample code above on my device, and here are the results:

    * My app with shipped ads: shows actual ads – I guess because the app has been enabled for iAds & approved…?
    * The sample project above: shows test ads.

    If you guys run the sample project above on your device, does it show test ads or do you get the error?

    Did you guys go to iTunes connect and enable your iAd network contract?

    @gojia: Unfortunately I no longer have a 3.1.3 device to test that out on… if anyone else does and can help @gojia out (and let me know if I’ve made some kind of mistake in the project above), I’d greatly appreciate it!

    @Thang: Thanks for helping out @Asad!

    @MarkAtAR: When you get an EXC_BAD_ACCESS, there are two ways I usually try to track that down: 1) set breakpoints in the code to narrow down the area nearby where it occurs, and 2) enable NSZombieEnabled for the project, which sometimes help give a hint about why it’s occurring:

    http://www.cocoadev.com/index.pl?NSZombieEnabled

  50. Joseph Nardone (3 comments) says:

    Hi Ray:
    Thanks, I contacted Apple and they said to file a support ticket as something is not set right in the iTunes Connect portal for that one particular app. It probably has to do with the fact that it was an update to an app that was activated for iAds before iAds launched and then later rejected. All my newer apps work fine.

    I have another question for you about the iAds in your live app. When you look at the console for the live app can you tell how long after a “ad not received error” the banner attempts to reload. Is it 30 seconds like in the test ads or 3 minutes – the reported refresh rate for successful loads?

    Thanks again,

    Joseph Nardone

  51. MarkAtAR (3 comments) says:

    @Ray

    Thanks for the quick reply Ray. I have ads on multiple view controllers and the error I am getting is this:

    *** -[MenuViewController bannerViewDidLoadAd:]: message sent to deallocated instance 0×141430

    But MenuViewController is the first view controller and this error only happens if you go to the next view controller before the first ad is able to load. Then it gives me that error above with NSZombiesEnabled. I think I am misunderstanding what is being done by the ads in the background or I am doing something seriously wrong. Does this make any sense?

  52. Chris Dunphy (2 comments) says:

    Thank you for the great tutorial – it has been a life saver.

    I only have one (really annoying) problem left. When I changed my UITableViewController into a UIViewController, the TableView now longer seems to respond to my ToolBar hiding / unhiding.

    When the ToolBar is displayed, the last row of my table ends up hidden behind it.

    I am beating my head against the wall figuring out how to get the auto-sizing to work for the ToolBar.

    Everything else is working great, other than the last line of my table getting lost when the toolbar displays.

    Thoughts?

  53. samson (11 comments) says:

    @MarkAtAR: bad_access usually occurs when you use a pointer whose memory is already freed.

    Did you set your adBannerView.delegate = NULL in your viewcontroller’s dealloc? This, among many others, is a very common mistake.

    @Ray/Joseph,

    Looks like new apps are fine. Only upgraded ones have this issue.

    I also contacted ITC and haven’t received any feedback yet. But the feedback Joseph received makes sense.

    I’ll wait and see. Thanks.

  54. MarkAtAR (3 comments) says:

    @samson

    Yup, I have self.adBannerView = nil in my dealloc. And dealloc is definitely running when I leave the view, but the error still occurs.

  55. samson (11 comments) says:

    @MarkAtAR, yes you set the view to nil. But did you set the delegate to nil too? e.g., self.adBannerView.delegate = nil;

    According to documents, it’s very important to set the delegate to nil in dealloc because it is possible that the viewcontroller is dealloc’ed but adview is being held by something else and its reference count is still above 0. In that case, when something happens in adview and it needs to call the delegate, it doesn’t know the delegate is freed, hence the crash.

  56. macjasp (1 comments) says:

    Fantastic. One of the most concise and well written articles i’ve ever read. Explained it perfectly and I now have iAds intergrated into my already existing App.

    It’s now going to be interesting to see what I make with iAds rather than selling my App at a price – but that’s a different topic

  57. Thang Tran (15 comments) says:

    @macjasp: yeah, now we have more reasons to provide free apps on the App Store. We can earn money while we do branding on the App Store.

  58. Ray Wenderlich (399 comments) says:

    @Joseph: Ah good to know re: the support ticket – that explains why I didn’t see any problems I guess!

    Just tested out the refresh rate on my app with ads. Ran it in debug mode on my local device, and after getting a didFailToReceiveAdWithError callback, it appeared to keep trying every 30 seconds thereafter.

    @samson: Thanks for helping out @Mark! @Mark, did that resolve it for you?

    @Chris: How have you added the toolbar, is it part of the view controller and added/removed to the view dynamically? If you have the table view and toolbar in the same view controller, can’t you just set the frame of the table view to the right size when the toolbar displays?

    Just checked out your blog by the way – looks pretty interesting and a cool lifestyle you lead, I’ll be following your site from now on! :]

    @Thang/@mac: Personally I’ve had better luck with regular app sales than with iAds… however iAds are a helpful addition to the free apps I make with the goal of enticing people to upgrade for the full versions. However, it really depends on your app – and how many people you can get to download it! :]

  59. Thang Tran (15 comments) says:

    @Ray: yeah, you are right! This is the key point no matter if your app is free for paid :)

  60. samson (11 comments) says:

    Update on my app:

    1. It gets rejected because my app doesn’t hide the ad bar when the app fails to receive an ad. Looks like “hide when no ad” becomes a mandatory requirements. (Mine is actually a little special — I put ad in UITableView.tableHeaderView so it can be scrolled away. I don’t want to hide the ad because it will shift the rows and might cause user to tap a wrong row if ad update happens about the same time of tapping. Anyways I’m trying other method to comply.)

    2. From yesterday, my app on device no longer receives “invalid app id/account” error. Now it still never gets an ad, and didFailToReceiveAd… is still called all the time. Only difference is that the error msg now becomes:

    Error Domain=ADErrorDomain Code=0 “no qualified ads found for this request” UserInfo=0×13871140 {NSLocalizedDescription=no qualified ads found for this request}

    I guess Apple support team might have done something to my account and this error looks more normal… But it still doesn’t see the test ad. I’m waiting for their further response.

  61. Forrest (5 comments) says:

    @samson in order to hide ads if errors happen, maybe like this

    - (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
    {
    NSLog(@”ad is wrong “);
    if( error != nil ){
    if( bannerView != nil ){
    bannerView.hidden = YES;
    }
    }
    }

    @all
    Actually regarding the delegate, so odd that they seems like do not run , for example, I put NSLog(..) to print info inside those three delegates,

    - (BOOL)bannerViewActionShouldBegin:(ADBannerView *)banner willLeaveApplication:(BOOL)willLeave
    {
    NSLog(@”Banner view is beginning an ad action”)
    return TRUE;
    }

    - (void)bannerViewDidLoadAd:(ADBannerView *)banner {
    NSLog(@”ad is okay “);
    if( bannerView != nil ){
    bannerView.hidden = NO;
    }
    }

    - (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
    {
    NSLog(@”ad is wrong “);
    if( error != nil ){
    if( bannerView != nil ){
    bannerView.hidden = YES;
    }
    }
    }

    Tested on simulator, there was no any info printed out, just something like these , is that wrong ?

    ADManager: did enter background

    The view controller returned NO from -shouldAutorotateToInterfaceOrientation: for all interface orientations. It should support at least one orientation.

  62. Samson (11 comments) says:

    @Forrest, thanks for the info. I know how to hide the ad upon failure, just not sure if I want to do it — I put ad in tableheaderview of uitableview, if I hide/show ad, user will see the table shift up or down 50 px. If user is about to tap on a row, he/she might tap on the wrong one because of the shift. Maybe I should rethink the overall design — but I really like the way the user can scroll the ad off screen.

    For your other question — did you check all the simple things first, such as:

    - you did set delegate to self
    - you did set ad content size set, and current size
    - you did call request ad on adbannerview

    I know these are just wild guesses… But it doesn’t hurt to check simple things first.

    (btw the auto rotate error is irrelevant to iAd and is not important. See my earlier comment)

  63. Ray Wenderlich (399 comments) says:

    @samson: Good to know that hide when no ad is mandatory! I’m glad that first error went away, keep us posted and let us know if/when it starts showing up actual ads.

    Regarding your ad placement, why not implement it the same way as in this tutorial where there’s a banner view that appears above the table view? Personally I like having it permanently showing even if the user scrolls, because after all you don’t get any $$ if the user can’t see it! :]

    @Forrest: That’s not the best way to hide the banner view when the ad isn’t showing, it’s best to remove the view or move it offscreen because otherwise you just have a blank spot where the ad is supposed to be. I mentioned one way to do so in the above tutorial.

    Samson has some good ideas about how to narrow down where your problem is. If none of those work, try the code from the sample project which will give you a working base to start with.

  64. Ricky (5 comments) says:

    Hi Ray, thanks so much for the tutorial, really helpful!
    I’ve only a question : i’m creating an app that has a lot of table views, navigation controller-based. Have I to put an iAd on any table view or only on the root view? What does Apple reccomends? Thanks very much :)

  65. Ray Wenderlich (399 comments) says:

    @Ricky: I haven’t seen an Apple doc with any specific recommendations of whether to put it just on the root view or all views… but personally, my rule of thumb is if there’s a good chance the user will be looking at a view for a period of time, in my apps I’ll put an ad view there, even if on multiple drill-down views. After all, more ads = more $$!

  66. Bob Schoenburg (6 comments) says:

    Hi Ray,

    Thanks to you, my app is now up.

    http://itunes.apple.com/us/app/icoin-free/id381816540?mt=8

  67. samson (11 comments) says:

    @ricky, I have similar app structure — many levels hierarchy of UITableView. I put Ad in every of them.

    Just make sure you implement viewDidLoad/viewDidUnload pair properly so that your ad views in invisible views are freed when memory is low, and recreated when that view goes visible again. Otherwise you might run out of memory sooner than you should.

    Usually you just need to set all subview properties to nil — assume you declare them as “retain” property and recreate them in viewDidLoad.

    @ray, yes i probably should just make it simpler and put the ad view in a constant area. The reason of my current design was that I thought the screen was small enough…

  68. Ricky (5 comments) says:

    First of all, thanks so much for your answers.
    @Ray i haven’t seen it too and it was for this reason that i asked for. So, i’ll put adbanners wherever i’ll suppose they would be helpful. Thanks :)

    @samson: Yes, I set them to nil in the dealloc section and I create them in viewDidLoad. If banner is available I show it, else the frame of the adbanner view in out of the view and it’s set to hidden. In the tableviews, i don’t have the problem of resize the view if the banner is hidden because it stays in a separate view and i can scroll the table view up/down separately. But the $$ comes only if the user tap on the banner view?
    Really thanks :)

  69. Ricky (5 comments) says:

    Sorry for the double post… but so, in any view i want the iAd i have to create a view implement the banner and all its methods?

  70. samson (11 comments) says:

    @ricky, yes you need to implement the ad banner delegate for every view controller. All my views inherit from a super class I created, so I just do it once in that super class. Maybe creating a category in UIViewController is an option too.

    Regarding setting view to nil, doing it in dealloc is necessary but might not be sufficient. You should do it in viewDidUnload too. The logic here is that, when a viewcontroller becomes invisible (e.g., another view pushed into the top of navigation controller), the system might choose to free its views in case of low memory (but will not release the view controller object itself). When the system does so, it will call viewDidUnload. You should then release anything you explicitly created in viewDidLoad or held in a retained property. The next time this view controller goes visible again (e.g., the view that covers it gets popped out), viewDidLoad will be called again.

    I think most apps with iAd will create iAd view manually in viewDidLoad, so you should do the opposite in viewDidUnload. Otherwise the system’s memory management won’t work efficiently (worse, it could cause memory leak if you don’t realize viewDidLoad could be called multiple times in a viewcontroller’s lifecycle).

    You can test this behavior by adding log statement in viewDidLoad/viewDidUnload, and use simulator’s “simulate low memory warning” menu. You’ll notice that viewDidUnload of the invisible views are called when you choose this menu item, and viewDidLoad called upon when it goes visible again.

    For more details please refer to “memory management” paragraph of http://developer.apple.com/iphone/library/documentation/uikit/reference/UIViewController_Class/Reference/Reference.html

  71. Ricky (5 comments) says:

    Thanks samson, you are really really helpful!! :)

  72. Ray Wenderlich (399 comments) says:

    @Bob: Awesome, congrats!

    @Ricky: Yeah, similar to what Samson mentioned, for my apps I pulled the above code into a helper object to avoid all the code duplication.

    @Samson: Agreed, thanks for helping out Ricky here! Good point on the viewDidLoad/viewDidUnload scenario, that is something that took me a long time to figure out with iPhone dev :]

  73. Chris Dunphy (2 comments) says:

    Hi Ray –

    The quality of conversation and help in this thread is phenomenal – I love it.

    You asked: “How have you added the toolbar, is it part of the view controller and added/removed to the view dynamically?”

    I have a MainWindow.xib that has a Navigation Controller, featuring a Navigation Bar, a Toolbar, and a Root View Controller.

    The RootViewController.xib is now set up with a Table View contained within a View, contained within a view – as instructed in the tutorial here.

    “If you have the table view and toolbar in the same view controller, can’t you just set the frame of the table view to the right size when the toolbar displays?”

    I’m probably doing something simple wrong. The Table View used to resize automatically before I changed things to incorporate the iAd. Now the bottom row ends up obscured when the Toolbar slides up.

    “Just checked out your blog by the way – looks pretty interesting and a cool lifestyle you lead, I’ll be following your site from now on! :]”

    *blush* – Thanks! Nomadic life is fun indeed. I look forward to tracking each others blogs.

    Thanks for everything,

    – Chris

  74. samson (11 comments) says:

    @ray & ricky, you are welcome!

    BTW yesterday my app was approved! It’s still the “ad in the tableheaderview and can be scrolled away” design. My final solution to avoid row shifting cause by ad bar hide/show is to use a static logo bar when ad is not available. Looks like app reviewers are fine with it as long as a blank ad bar is not shown.

    Now the iAd fill rate is really low, like 5-7% for me. But each click generates $1.x income. Not bad. eCPM shows $300+ (note: this doesn’t mean you’ll get $300 for every 1000 impressions. It’s a reversed calculated value by dividing your income of all clicks and impressions by number of impressions. It can’t be used to predict or calculate your income. Took me a while to understand this).

    I believe the rate will decrease as more apps support iAd. Also there are only 2 ads right now, dove + men and nissan leaf car. I’m kind of bored to see them everyday. Hope more advertisers join.

  75. Bob Schoenburg (6 comments) says:

    Does iAds work on ALL iPods or only those with certain specs?

  76. Philip (2 comments) says:

    Hi Ray,

    I have a question. When I put this into my app, I get a warning on:
    [self createAdBannerView]; in the viewDidLoad method,
    and the same warning on this line:
    [self refresh]; in the viewWillAppear method.

    The warning says, “‘iCharacterViewController’ may not respond to ‘-refresh’” and

    ‘iCharacterViewController’ may not respond to ‘-createAdBannerView’.

    The app crashes whenever it is opened…

    Help? Anyone?

  77. Ray Wenderlich (399 comments) says:

    @Chris: Hm, the first thing I’d check is if the autosizing properties are set up properly for your RootViewController’s view, subview, and table view controller. Yeah autosizing can be a pain to get working right sometimes…

    @samson: Awesome, glad your app got approved! Fill rate is 15% on one of my apps, and 7% on another. I’m not sure why there’s a difference! But either way agreed that is pretty low eh? I’ve heard some people are showing ads from other networks when iAds aren’t available?

    @Bob: iAds only work on iOS 4.0+.

    @Philip: That warning means the compiler can’t find the refresh and createAdBannerView methods in your class. Have you added those methods? If all else fails, try stepping through with your debugger.

  78. Jim Murff (6 comments) says:

    Thanks again Ray. You are awesome! I made some slight changes if you want ads to appear at the bottom.
    Thought I’d share it to save someone else the trouble.

    I get the warning too about -shouldAutorotateToInterfaceOrientation:. I researched it on developers forum. It’s a known bug in the iAd framework.

    Here is code with ability to to do Bottom display. Just use the define to activate relevant code:

    // Comment out or undef to use iAds at top of screen.
    #define __BOTTOM_iAD__

    -(void)createAdBannerView
    {
    Class classAdBannerView = NSClassFromString(@"ADBannerView");
    if (classAdBannerView != nil)
    {
    self.adBannerView = [[[classAdBannerView alloc]
    initWithFrame:CGRectZero] autorelease];
    [adBannerView setRequiredContentSizeIdentifiers:[NSSet setWithObjects:
    ADBannerContentSizeIdentifier320x50,
    ADBannerContentSizeIdentifier480x32, nil]];

    if (UIInterfaceOrientationIsLandscape([UIDevice currentDevice].orientation))
    {
    [adBannerView setCurrentContentSizeIdentifier:ADBannerContentSizeIdentifier480x32];
    }
    else
    {
    [adBannerView setCurrentContentSizeIdentifier:ADBannerContentSizeIdentifier320x50];
    }

    #ifdef __BOTTOM_iAD__
    // Banner at Bottom
    CGRect cgRect =[[UIScreen mainScreen] bounds];
    CGSize cgSize = cgRect.size;
    [adBannerView setFrame:CGRectOffset([adBannerView frame],
    0, cgSize.height + [self getBannerHeight])];
    #else
    // Banner at the Top
    [adBannerView setFrame:CGRectOffset([adBannerView frame],
    0, -[self getBannerHeight])];
    #endif

    [adBannerView setDelegate:self];

    [self.view addSubview:adBannerView];
    }
    }

    -(void)fixupAdView:(UIInterfaceOrientation)toInterfaceOrientation
    {
    if (adBannerView != nil)
    {
    if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation))
    {
    [adBannerView setCurrentContentSizeIdentifier:ADBannerContentSizeIdentifier480x32];
    }
    else
    {
    [adBannerView setCurrentContentSizeIdentifier:ADBannerContentSizeIdentifier320x50];
    }

    [UIView beginAnimations:@"fixupViews" context:nil];
    if (adBannerViewIsVisible)
    {
    CGRect adBannerViewFrame = [adBannerView frame];
    adBannerViewFrame.origin.x = 0;
    #ifdef __BOTTOM_iAD__
    CGRect cgRect =[[UIScreen mainScreen] bounds];
    CGSize cgSize = cgRect.size;
    adBannerViewFrame.origin.y = cgSize.height -
    [self getBannerHeight:toInterfaceOrientation];
    #else
    adBannerViewFrame.origin.y = 0;
    #endif
    [adBannerView setFrame:adBannerViewFrame];

    CGRect contentViewFrame = contentView.frame;
    #ifdef __BOTTOM_iAD__
    contentViewFrame.origin.y = 0;
    #else
    contentViewFrame.origin.y = [self getBannerHeight:toInterfaceOrientation];
    #endif
    contentViewFrame.size.height = self.view.frame.size.height -
    [self getBannerHeight:toInterfaceOrientation];
    contentView.frame = contentViewFrame;
    }
    else
    {
    CGRect adBannerViewFrame = [adBannerView frame];
    adBannerViewFrame.origin.x = 0;
    #ifdef __BOTTOM_iAD__
    CGRect cgRect =[[UIScreen mainScreen] bounds];
    CGSize cgSize = cgRect.size;
    adBannerViewFrame.origin.y = cgSize.height;
    #else
    adBannerViewFrame.origin.y = 0;
    #endif
    [adBannerView setFrame:adBannerViewFrame];

    CGRect contentViewFrame = contentView.frame;
    contentViewFrame.origin.y = 0;
    contentViewFrame.size.height = self.view.frame.size.height;
    contentView.frame = contentViewFrame;
    }

    [UIView commitAnimations];
    }
    }
  79. Ray Wenderlich (399 comments) says:

    @Jim: Awesome, thanks so much for sharing, I’m sure it will be useful to others as well!

  80. JDizzle (1 comments) says:

    I also recently submitted an app in the store that implements the ad banner view. I guess I just want to reiterate what samson said:

    ** Apple REQUIRES that you hide your banner ad if no ads are being served, FOR NOW. This is NOT explicitly mentioned in the documentation. If you don’t do this, the app will get rejected. **

    And yes, there aren’t that many ads being served yet. I think that’s part of the reason why they are rejecting apps that don’t hide the banner view (since there aren’t that many ads being served yet, to begin with).

  81. Ricky (5 comments) says:

    Yes, Apple wants that, and i think he’s right. I need to use the iAd banner in the bottom of the screen, so tomorrow i’ll try the code Jim Murff, thank you so much for sharing :)
    Sorry for the Off Topic, but my app makes an export of the datas on some files with a personalized extension, but i don’t know how set the document icon for these files. I’ve looked all around the developer site and the web without find a solution, somebody of could help me? :(

  82. Adrian Thompson (1 comments) says:

    Great tutorial, not sure if this has already been asked/answered but what are your thoughts on iAd placement within an app?

    Some views require as much screen real-estate as possible for your app and adding an iAd is simple too much. Do you think adding an iAd within an ‘about app’ view for example would be frowned upon by Apple? I know to benefit the developer its about getting an much exposure to the ad as possible but wondered what peoples thoughts were on this.

    Thanks

    Adrian

  83. ltgbau (2 comments) says:

    My app is ready for sales but iAds isn’t good as I think before, revenue is worse than AdMob :(

  84. Gucman (1 comments) says:

    Hi
    The artice is great. I finally managed to integrate iAd into my game. Everything runs smoothly (it works also on pre 4.0 without apps) but I have problem with clicking the ad.
    When I click the ad the correct callback is called as it should be (implementation is below)
    [code] - (BOOL)bannerViewActionShouldBegin:(id)banner willLeaveApplication:(BOOL)willLeave
    {
    GLOG(@"bannerView Begin");
    if (!willLeave)
    [game pauseAndStopMusic];
    return YES;
    }[/code]
    But nothing changes (except game is paused and music stops) in displaying ad (I can’t see no “action”). Also there should be called other callback some time after clicking ad informing its action is over. I have implemented it like so:
    [code]
    - (void)bannerViewActionDidFinish:(id)banner
    {
    GLOG(@"bannerView End");
    [game continueWithMusic];
    }[/code]
    It is never called. Have You any idea why it is not called? I have tested it on the simulator and real device (iPod Touch) and the results are the same. Is it possible that those “test ads” are bad? I belive there is no possibility to have real ads during development – only distributed apps via AppStore have real ads, am I right?

  85. Ray Wenderlich (399 comments) says:

    @JDizzle: Good to know that hiding is actually required – thank goodness I happened to do it that way when I submitted my app! :]

    @Ricky: Have you tried setting the document icon for your app according to this guide?

    http://developer.apple.com/iphone/library/documentation/userexperience/conceptual/mobilehig/IconsImages/IconsImages.html

    @Adrian: AFAIK it’s up to you as a developer where you put your ads, so I don’t think there would be any problem putting it in an about view. But of course, I doubt users would go there much, which means you wouldn’t make as much revenue…

    @itgbau: Yeah will be interesting to see how this plays out in the long run…

    @Gucman: Interesting, I haven’t seen that problem. I’d try the sample project above and make sure the ads show up for you OK there – if they do it may be something specific to your project that is preventing the ads from displaying.

  86. Bill Seyler (1 comments) says:

    Such a great tutorial. I was able to retrofit my app in about 2 hours (and I had to do the rework of several table views)!

    Great job Ray!

    Bill

Leave a Comment

I'd love to hear your thoughts!

1 nhận xét:

  1. If you're looking for an excellent contextual ad network, I suggest you check out Chitika.

    Trả lờiXóa