前回の続き。

今回はAdMobで提供されるバナー広告をアプリ内で表示する方法を説明します。

以下の本家ドキュメントを参考にしました。

GADBannerViewを画面下に表示する

よくみかけるアプリの画面下部に固定でバナー広告表示する方法を説明します。バナー広告表示はGADBannerViewクラスで行います。

InterfaceBuilder上に配置されたViewControllerを開きます。Use Size Classesはとりあえずオフにしてください。説明がややこしくなるので。そもそもデフォルトはオフで良いと思うんですが・・・

gad002

Viewをドラッグ&ドロップして配置します。配置したらPinボタンを押して、以下のようにConstraintを設定します。

gad003

これでサイズと下部固定のConstraintは設定しました。次はAlignボタンを押して画面の水平方向中心に配置するConstraintを設定します。

gad004

Resolve Auto Layout Issuesを押して、Update Framesを選択すると、下部固定のViewが配置されます。

gad006

gad007

このままではただのViewなので、これをGADBannerViewにします。Identity inspectorのClassをGADBannerViewにしてください。

gad008

GADBannerViewが広告を表示するようにする

ViewControllerでGADBannerViewをimportします。

#import "GADBannerView.h"

ViewControllerでGADBannerViewをpropertyとして宣言。IBOutletにして、Interface Builder上のGADBannerViewとひもづけます。

@property (weak, nonatomic) IBOutlet GADBannerView *bannerView;

viewDidLoadで以下のようにすればGADBannerViewに広告が表示されるはずです。

_bannerView.adUnitID = @"TODO:AdMob管理画面で登録したバナー広告のUnit ID";
// 通常、GADBannerViewを配置しているViewController(self)を指定
_bannerView.rootViewController = self;
// 広告をリクエストするオブジェクトを生成
GADRequest *request = [GADRequest request];
// テスト広告を表示する端末を指定
request.testDevices = @[GAD_SIMULATOR_ID];
// 広告読み込みを開始する
[_bannerView loadRequest:request];

testDevicesにDeviceのIdentifierを指定すると、その端末では実際の広告ではなくテスト広告が表示されます。テスト端末で広告をタップして集計対象になるのを避けたい場合は指定します。GAD_SIMULATOR_IDを指定するとSimulatorでテスト広告が表示されます。実際の広告がどんなものか見たいときは指定しないほうがよいでしょう。

とりあえず表示はされましたが、いくつか問題があります。

  • 広告取得するまでの間空白表示になる。これは申請時にリジェクト要因になりうる
  • ネットワークエラーなどで広告取得できなかったら、その後ネットワーク接続しても広告が表示されない状態が続く

GADBannerViewのイベントを受け取る

GADBannerViewDelegateプロトコルで各種イベントを受け取れます。通常はViewControllerで実装することになるでしょう。

@interface PSDebugAdMobViewController
// ViewController(self)をdelegateに設定
_bannerView.delegate = self;

いくつかのDelegateメソッドがありますが、使用するのは主に次の2つでしょう。

– (void)adViewDidReceiveAd:(GADBannerView *)bannerView: 広告を受信した
– (void)adView:(GADBannerView *)view didFailToReceiveAdWithError:(GADRequestError *)error 広告受信に失敗した

ドキュメントによるとGADBannerViewを生成しhasしているクラスのdeallocで以下のようにdelegateをnilにしたほうが無難なようです。

- (void)dealloc {
    _bannerView.delegate = nil;
}

ViewControllerがdeallocされるタイミングでGADBannerViewもdeallocされdelegateも無効になるとは思いますが、ドキュメントにあるので念のため。

広告受信したときにバナー表示する

広告受信するまでバナー領域が空白表示になる問題。その対処法のひとつ。

  • 広告受信していない時、コンテンツ(広告以外の要素)を画面いっぱいに表示する
  • 広告受信したら、コンテンツの下端をバナー分上にずらして、バナー表示する

このような実装は以下のようにして実現できます。

  • ViewControllerのViewの上に、コンテンツ表示用のViewを配置します
  • コンテンツ表示用のViewのLeading,Trailing,Top,Bottom Constraintを全部0pxにして、画面全体に表示されるようにする
  • Bottom ConstraintをIBOutlet NSLayoutConstraintで宣言したプロパティとひもづける(contentsBottomConstraintとする)
  • GADBannerViewを下端に配置(さきほど説明したのと同じ手順)
  • GADBannerView#hidden = YESにしておく
  • GADBannerViewDelegate#adViewDidReceiveAd:を受け取ったら、contentsBottomConstraint.constantを50に設定し、
  • GADBannerView#hidden = NOにする

もうひとつの方法は、

  • 広告受信していない時、バナー領域に何らかの表示を行う。たとえばアプリのロゴや自社広告画像などアプリ内埋め込みのものを表示
  • 広告受信したら広告表示に切り替える

このような実装は以下のようにして実現できます。

  • GADBannerViewをメンバー変数としてhasするクラスを作る(MYBannerViewとする)
  • そのクラス内でGADBannerViewDelegateを受け取るようにする
  • MYBannerViewにGADBannerViewをaddSubviewしhidden = YESにしておく
  • MYBannerViewはデフォルトの表示を行う(アプリロゴなど)
  • 広告受信したら、GADBannerView#hidden = NOにして表示する

広告受信失敗したら再度広告リクエストを行う

これは以下のようにして実現できるでしょう。

- (void)adView:(GADBannerView *)view didFailToReceiveAdWithError:(GADRequestError *)error {
    __weak GADBannerView *weakBannerView = _bannerView;
    //
    // 5分後に再度広告をリクエストする
    //
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * 60.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        // 画面遷移してViewControllerおよび_bannerViewが開放されたら、weakポインタはnilになる。
        // その場合は無視する
        if (weakBannerView == nil) return;

        GADRequest *request = [GADRequest request];
        request.testDevices = @[GAD_SIMULATOR_ID];
        [weakBannerView loadRequest:request];
    });
}