Tab Bar の上に AdMob バナー広告を表示する

昔作った iPhone 向けのアプリにバナー広告を Tab Bar の上へ追加したのですが、そのときにかなり時間がかかってしまったので、備忘録を残しておきます。SwiftUI ではなく、Storyboard でのお話です。

### 完成図
Tab Bar の上に広告が表示され、タブを切り替えても同じ広告が表示されます。

![screen.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853334/b6b617d4-bcdb-d85f-8e1d-01cf5a00d474.png)

### 環境
Xcode: 15.2
Firebase Apple SDK: 10.28.0

### 画面構成 (Storyboard)
画面の下にタブの切り替えボタンがあるごく普通のレイアウトです。View Controller は、わかりやすく背景に色をつけた View を配置しています。青と赤の画面では、ボタンを配置し、それぞれ緑・黄色の画面へ遷移できるようにしています。

Tab Bar Controller -> Navigation Controller -> View Controller x 2
![Storyboard0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853334/250d9347-fca9-ebdb-6d92-d4f706b35efd.png)

### アイデア
Tab Bar の上にそのままバナー広告を配置すると、View Controller に配置された View が広告によって隠れてしまうため、バナー広告の高さ分だけ、Navigation Controller の Safe Area に余白を設ける。

#### 1. Tab Bar の上にバナー広告を配置
AdMob 公式 Web サイト([Get Started](https://developers.google.com/admob/ios/quick-start)) を参考にバナー広告を作成し、tabBar の上に配置されるように Constraint を設定します。

```swift:TabBarController.swift
class TabBarController: UITabBarController{
   var adSize = CGSize.zero
   var bannerView: GADBannerView!
   var adaptiveSize: GADAdSize!
   
   override func viewDidLoad() {
      super.viewDidLoad()
      let viewWidth = self.view.frame.inset(by: self.view.safeAreaInsets).width
      adaptiveSize = GADCurrentOrientationAnchoredAdaptiveBannerAdSizeWithWidth(viewWidth)
      adSize = adaptiveSize.size
      GADMobileAds.sharedInstance().start(completionHandler: nil)
      bannerView = GADBannerView(adSize: adaptiveSize)
      addBannerViewToView(bannerView)
      bannerView.adUnitID = "ca-app-pub-3940256099942544/2435281174"
      bannerView.rootViewController = self
      bannerView.load(GADRequest())
   }
   // tabBar の上に Banner 広告を配置
   private func addBannerViewToView(_ bannerView: GADBannerView) {
      bannerView.translatesAutoresizingMaskIntoConstraints = false
      view.addSubview(bannerView)
      view.addConstraints(
         [NSLayoutConstraint(item: bannerView,
                             attribute: .bottom,
                             relatedBy: .equal,
                             toItem: tabBar,
                             attribute: .top,
                             multiplier: 1,
                             constant: 0),
          NSLayoutConstraint(item: bannerView,
                             attribute: .centerX,
                             relatedBy: .equal,
                             toItem: view,
                             attribute: .centerX,
                             multiplier: 1,
                             constant: 0)]
      )
   }
}
```
わかりにくいですが、この状態だと View Controller に配置した 青い View の下部がバナー広告とかぶっている状態です。

![adShow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853334/b4284e57-d600-2078-067a-1ccfd9564cde.png)

#### 2. Navigation Controller の下側に余白を設ける
Safe Area の Bottom へ広告の高さ分の余白を追加することで、広告と被らないようにします。

```swift:NavigationController.swift
class NavigationController: UINavigationController{
   override func viewDidLoad() {
      super.viewDidLoad()
      if let tabBarController = self.parent as? TabBarController {
         let adHeight = tabBarController.adSize.height
         additionalSafeAreaInsets = UIEdgeInsets(top: 0, left: 0, bottom: adHeight, right: 0)
      }
   }
}
```
[additionalSafeAreaInsets](https://developer.apple.com/documentation/uikit/uiviewcontroller/2902284-additionalsafeareainsets) とは、既存の Safe Area の Inset に追加で適用される余白を指定するプロパティです。Safe Area とは、画面の端（例えば、ノッチやホームインジケーターなど）やその他のUI要素と重ならない安全な領域を指します。なお、Inset は、UI要素の境界から内部に設定する余白やパディングのことです。

![adSpace.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853334/fc70b83d-c2d8-4d12-4bb8-760d84158026.png)

作成した余白に広告を配置することで、View と 広告がかぶることなく表示され、冒頭の画像のような状態になります。なお、青や赤画面などを表示している View Controller 側に特別な処理は必要ありません。

### 利点
Tab Bar Controller で広告を表示しているので、タブを切り替えても同じ広告が表示され続けます。加えて、Navigation Controller で additionalSafeAreaInsets を指定しているので、画面遷移が発生しても画面の下の余白は保持されます。

サンプルアプリでは、青画面にあるボタンをタップすることで、緑色の画面へ遷移しますが、以下の画像のように画面下部の余白は保持されます。

![move.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853334/d556906e-e2e1-3a8b-f10a-cad2ca06f3ba.png)

### さいごに
これより簡単な方法があるかはわかりませんが、とりあえずやりたいことはできたので、よしってかんじです。もし間違っているところがあればご指摘お願いします。

この方法で、バナー広告を実装したアプリは、↓なので、興味があれば使ってみてください。
給与管理アプリ：
https://apps.apple.com/jp/app/%E7%B5%A6%E4%B8%8E%E7%AE%A1%E7%90%86%E3%82%A2%E3%83%97%E3%83%AA/id1443054271

### 参考URL
https://web-y.dev/2020/11/07/ios-always-showable-view-on-uitabbar/

https://developers.google.com/admob/ios/banner

https://firebase.google.com/support/release-notes/ios

https://developers.google.com/admob/ios/rel-notes