0

When there is no iAd banner to display, we would like to display a UIWebView of the same dimensions pointed to a specific URL.

However, hiding the iAd banner and showing the UIWebView doesn't work. We embed the show/hide code inside bannerViewDidLoadAd and didFailToReceiveAdWithError. All that appears is the white, blank rectangle when there is no iAd inventory instead of our UIWebView.

If a user clicks on a link inside the UIWebView, we would like the link to open in Safari. Do we need to add a delegate to the UIWebView?

Code:

//
//  SAiOSAdPlugin.m
//  Ad Plugin for PhoneGap
//
//  Created by shazron on 10-07-12.
//  Copyright 2010 Shazron Abdullah. All rights reserved.
//  Cordova v1.5.0 Support added 2012 @RandyMcMillan
//

#import "SAiOSAdPlugin.h"

//#ifdef CORDOVA_FRAMEWORK
#import <Cordova/CDVDebug.h>
//#else
//#import "CDVDebug.h"
//#endif

@interface SAiOSAdPlugin(PrivateMethods)

- (void) __prepare:(BOOL)atBottom;
- (void) __showAd:(BOOL)show;

@end


@implementation SAiOSAdPlugin

@synthesize adView;
@synthesize bannerIsVisible, bannerIsInitialized, bannerIsAtBottom, isLandscape;

#pragma mark -
#pragma mark Public Methods

- (void) resizeViews
{
    Class adBannerViewClass = NSClassFromString(@"ADBannerView");
    if (adBannerViewClass && self.adView)
    {
        CGRect webViewFrame = [super webView].frame;
        CGRect superViewFrame = [[super webView] superview].frame;
        CGRect adViewFrame = self.adView.frame;

        BOOL adIsShowing = [[[super webView] superview].subviews containsObject:self.adView];
        if (adIsShowing) 
        {
            if (self.bannerIsAtBottom) {
                webViewFrame.origin.y = 0;
                CGRect adViewFrame = self.adView.frame;
                CGRect superViewFrame = [[super webView] superview].frame;
                adViewFrame.origin.y = (self.isLandscape ? superViewFrame.size.width : superViewFrame.size.height) - adViewFrame.size.height;
                self.adView.frame = adViewFrame;
            } else {
                webViewFrame.origin.y = adViewFrame.size.height;
            }

            webViewFrame.size.height = self.isLandscape? (superViewFrame.size.width - adViewFrame.size.height) : (superViewFrame.size.height - adViewFrame.size.height);
        } 
        else 
        {
            webViewFrame.size = self.isLandscape? CGSizeMake(superViewFrame.size.height, superViewFrame.size.width) : superViewFrame.size;
            webViewFrame.origin = CGPointZero;
        }

        [UIView beginAnimations:@"blah" context:NULL];
        [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];

        [super webView].frame = webViewFrame;

        [UIView commitAnimations];
    }
}

- (void) orientationChanged:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
{
    NSInteger orientation = [[arguments objectAtIndex:0] integerValue];

    switch (orientation) {
        // landscape
        case 90:
        case -90:
            self.isLandscape = YES;
            break;
        // portrait
        case 0:
        case 180:
            self.isLandscape = NO;
            break;
        default:
            break;
    }

    Class adBannerViewClass = NSClassFromString(@"ADBannerView");
    if (adBannerViewClass && self.adView)
    {
        self.adView.currentContentSizeIdentifier = self.isLandscape ? ADBannerContentSizeIdentifierLandscape : ADBannerContentSizeIdentifierPortrait;
        [self resizeViews];
    }
}

- (void) prepare:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
{
    NSUInteger argc = [arguments count];
    if (argc > 1) {
        return;
    }

    NSString* atBottomValue = [arguments objectAtIndex:0];
    [self __prepare:[atBottomValue boolValue]];
}

- (void) showAd:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
{
    NSUInteger argc = [arguments count];
    if (argc > 1) {
        return;
    }

    NSString* showValue = [arguments objectAtIndex:0];
    [self __showAd:[showValue boolValue]];
}

#pragma mark -
#pragma mark Private Methods

- (void) __prepare:(BOOL)atBottom
{
    NSLog(@"SAiOSAdPlugin Prepare Ad At Bottom: %d", atBottom);

    Class adBannerViewClass = NSClassFromString(@"ADBannerView");
    if (adBannerViewClass && !self.adView)
    {
        self.adView = [[ADBannerView alloc] initWithFrame:CGRectMake(0, 0, 320, 50)];
        // we are still using these constants even though they are deprecated - if it is changed, iOS 4 devices < 4.3 will crash.
        // will need to do a run-time iOS version check 
        self.adView.requiredContentSizeIdentifiers = [NSSet setWithObjects: ADBannerContentSizeIdentifierPortrait, ADBannerContentSizeIdentifierLandscape, nil];        

        self.adView.delegate = self;

        NSString* contentSizeId = (self.isLandscape ? ADBannerContentSizeIdentifierLandscape : ADBannerContentSizeIdentifierPortrait);

        self.adView.currentContentSizeIdentifier = contentSizeId;

        if (atBottom) {
            self.bannerIsAtBottom = YES;
        }

        self.bannerIsVisible = NO;
        self.bannerIsInitialized = YES;

        self.houseAdView = [[UIWebView alloc] initWithFrame: CGRectMake(0.0, 0.0, 1.0, 1.0)];
        self.houseAdView.frame = self.adView.frame;
        NSURLRequest *request = [[NSURLRequest alloc] initWithURL: [NSURL URLWithString: @"http://www.panabee.com"]];
        [self.houseAdView loadRequest: request];

    }
}

- (void) __showAd:(BOOL)show
{
    NSLog(@"SAiOSAdPlugin Show Ad: %d", show);

    if (!self.bannerIsInitialized){
        [self __prepare:NO];
    }

    if (!(NSClassFromString(@"ADBannerView") && self.adView)) { // ad classes not available
        return;
    }

    if (show == self.bannerIsVisible) { // same state, nothing to do
        return;
    }

    if (show)
    {
        [UIView beginAnimations:@"blah" context:NULL];
        [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];

        [[[super webView] superview] addSubview:self.adView];
        [[[super webView] superview] bringSubviewToFront:self.houseAdView];
        [[[super webView] superview] bringSubviewToFront:self.adView];
        [self resizeViews];

        [UIView commitAnimations];

        self.bannerIsVisible = YES;
    }
    else 
    {
        [UIView beginAnimations:@"blah" context:NULL];
        [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];

        [self.adView removeFromSuperview];
        [self resizeViews];

        [UIView commitAnimations];

        self.bannerIsVisible = NO;
    }

}

#pragma mark -
#pragma ADBannerViewDelegate

- (void)bannerViewDidLoadAd:(ADBannerView *)banner
{
    Class adBannerViewClass = NSClassFromString(@"ADBannerView");
    if (adBannerViewClass)
    {
        NSString* jsString =
        @"(function(){"
        "var e = document.createEvent('Events');"
        "e.initEvent('iAdBannerViewDidLoadAdEvent');"
        "document.dispatchEvent(e);"
        "})();";

        [banner setHidden:YES];
        [self.houseAdView setHidden:NO];

        [super writeJavascript:[NSString stringWithFormat:jsString]];
    }
}

- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError*)error
{
    Class adBannerViewClass = NSClassFromString(@"ADBannerView");
    if (adBannerViewClass)
    {
        NSString* jsString = 
        @"(function(){"
        "var e = document.createEvent('Events');"
        "e.initEvent('iAdBannerViewDidFailToReceiveAdWithErrorEvent');"
        "e.error = '%@';"
        "document.dispatchEvent(e);"
        "})();";

        [banner setHidden:YES];
        [self.houseAdView setHidden:NO];

        [super writeJavascript:[NSString stringWithFormat:jsString, [error description]]];
    }
}

@end
Undo
  • 25,519
  • 37
  • 106
  • 129
Crashalot
  • 33,605
  • 61
  • 269
  • 439

1 Answers1

0

It's me again :)

You're not adding it to the sub view tree.

From your __prepare method

self.houseAdView = [[UIWebView alloc] initWithFrame: CGRectMake(0.0, 0.0, 1.0, 1.0)];
self.houseAdView.frame = self.adView.frame;
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: [NSURL URLWithString: @"http://www.panabee.com"]];
[self.houseAdView loadRequest: request];

That's great. But it doesn't work - you are missing one line of code - a critical, vital line that gets every developer some time or another.

[self addSubview:self.houseAdView];

I'm making a few assumptions, like that self is a UIView. Test before shipping.

So, that part of your __prepare method should look like this:

self.houseAdView = [[UIWebView alloc] initWithFrame: CGRectMake(0.0, 0.0, 1.0, 1.0)];
self.houseAdView.frame = self.adView.frame;
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: [NSURL URLWithString: @"http://www.panabee.com"]];
[self.houseAdView loadRequest: request];
[self addSubview:self.houseAdView];
Community
  • 1
  • 1
Undo
  • 25,519
  • 37
  • 106
  • 129
  • you are awesome, undo, but now we are receiving a memory warning after adding that one line (addSubview). are we supposed to remove the subview in didFailToReceiveAdWithError and add it back in bannerViewDidLoadAd? this seems wasteful to create/remove the subview every time, but how do we avoid the memory warning? – Crashalot Jun 01 '13 at 20:40
  • @Crashalot Hrmmm... Yep. Go ahead and add it in `didFail` and remove it in `didAdd`. Seems weird you are getting a mem warning, though. – Undo Jun 01 '13 at 20:42
  • Is `__prepare` called multiple times? – Undo Jun 01 '13 at 20:43
  • _prepare should only get called once, but i can confirm. isn't it wasteful to add it in didfail and remove it in didadd? thanks again! – Crashalot Jun 01 '13 at 20:43
  • @Crashalot Yes, it is. I can't see, though, how *adding* it to the tree should trigger a mem warning - that's weird. You say that removing the line makes the mem warning go away? – Undo Jun 01 '13 at 20:45
  • yes, there was no mem warning before that line. now there is. also _prepare is only getting called once. – Crashalot Jun 01 '13 at 20:46
  • @Crashalot Sounds like you are just barely below the warning line, and this tiny bit of memory pushes you over the top. It's not a problem with this addition - everything is in compliance with best practices - there is something else chewing up memory. Try running with Instruments and see how the memory curve looks before and after the code change. – Undo Jun 01 '13 at 20:48
  • ok thanks, undo! one last question: we added this to make sure clicks in the UIWebView open in safari (-(BOOL) webView:(UIWebView *)inWeb shouldStartLoadWithRequest:(NSURLRequest *)inRequest navigationType:(UIWebViewNavigationType)inType { if ( inType == UIWebViewNavigationTypeLinkClicked ) { [[UIApplication sharedApplication] openURL:[inRequest URL]]; return NO; } return YES; }). but it's saying "Assigning to id from incompatible type SAiOSAdPlugin *" – Crashalot Jun 01 '13 at 20:53
  • @Crashalot Got it. In your `SAiOSAdPlugin.h`, you need to declare that the class conforms to the `UIWebViewDelegate` protocol. – Undo Jun 01 '13 at 20:56
  • OK, but just adding something like this? @protocol UIWebViewDelegate; – Crashalot Jun 01 '13 at 21:05
  • @Crashalot **No** That's if you want to make a *new* protocol. You just want to say you *conform* to a protocol. [This is a good read](http://stackoverflow.com/a/7617651/1849664) – Undo Jun 01 '13 at 21:06
  • You just want the stuff in the <>'s in that answer. – Undo Jun 01 '13 at 21:08
  • thanks, we'll read it, and implement it. thanks again for all your help! btw, is this email addr the best way to contact you? physics@erwaysoftware.com. would like to ask two questions. – Crashalot Jun 01 '13 at 21:16
  • @crash I'd rather answer questions here on SO. Fire them this way! – Undo Jun 01 '13 at 21:20
  • sorry, these are qs more about consulting and your apps. – Crashalot Jun 01 '13 at 22:26
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/31049/discussion-between-undo-and-crashalot) – Undo Jun 01 '13 at 22:27