【SwiftUI】UIViewControllerでSwiftUIのViewを表示する

過去にUIKitで作って公開したアプリを、そっくりそのままSwiftUIに移行したかったがやり方がよくわからなかったので、表題の通りUIViewController上でSwiftUIのViewを表示しました。

UIViewController上で、SwiftUIで作ったContentViewを表示しています。.environmentObjectもここで初期化します。

override func viewDidLoad() {

        super.viewDidLoad()

        

        let vc: UIHostingController = UIHostingController(rootView: ContentView().environmentObject(Test()))

            self.addChild(vc)

            self.view.addSubview(vc.view)

            vc.didMove(toParent: self)

            vc.view.translatesAutoresizingMaskIntoConstraints = false

            vc.view.heightAnchor.constraint(equalToConstant: view.frame.height).isActive = true

            vc.view.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 0).isActive = true

            vc.view.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: 0).isActive = true

            vc.view.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true

}

        

実行すると、あたかもSwiftUIって感じになります。

【SwiftUI】Admob広告を実装する

SwiftUIでアドモブのバナー、インタースティシャル、リワード広告を実装する方法です。

とりあえずテスト広告を出したい!って方向けです。

2021年8月現在、問題なく動作いたします。

 

CocoaPods、GoogleMobileAdsの導入が終わっているものとして。FireBaseはよくわからないので入れてません。

 

ここら辺はよく忘れるやつ 

info.plistにGoogleの案内に載っているkeyの他に以下を追加。

   <key>NSAppTransportSecurity</key>

    <dict>

        <key>NSAllowsArbitraryLoads</key>

        <true/>

        <key>NSAllowsArbitraryLoadsForMedia</key>

        <true/>

        <key>NSAllowsArbitraryLoadsInWebContent</key>

        <true/>

    </dict>

AppDelegateを使わずに、アプリ名.swiftを使っている場合は以下を追加

import SwiftUI

import GoogleMobileAds

 

class AppDelegate: NSObject, UIApplicationDelegate {

    func application(_ application: UIApplication,didFinishLaunchingWithOptions launchOptions:

                        [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {

        GADMobileAds.sharedInstance().start(completionHandler: nil)

        return true

    }

}

 

@main

struct ADTestApp: App {

    var body: some Scene {

        WindowGroup {

            ContentView()

        }

    }

}

 

バナーを実装

表示用のViewを作ります。

import SwiftUI

import GoogleMobileAds

 

struct AdView: UIViewRepresentable {

    func makeUIView(context: Context) -> GADBannerView {

        let banner = GADBannerView(adSize: kGADAdSizeBanner)

        // 以下は、バナー広告向けのテスト専用広告ユニットIDです。自身の広告ユニットIDと置き換えてください。

        banner.adUnitID = "ca-app-pub-3940256099942544/2934735716"

        banner.rootViewController = UIApplication.shared.windows.first?.rootViewController

        banner.load(GADRequest())

        

        return banner

    }

    func updateUIView(_ uiView: GADBannerView, context: Context) {

    }

}

 

使い方

struct ContentView: View {

 

 

    var body: some View {

        AdView().frame(width: 320, height: 50)

    }

}

置きたいところに置くだけでOK。

kGADAdSizeBannerを指定しててもframeを大きくしておくと、大きなサイズで表示されるっぽい。

 

インタースティシャルを実装

オンアッパーで読み込んで出したいところで出す。

もちろん、画面が切り替わるタイミングで!

import SwiftUI

import GoogleMobileAds

 

struct ContentView: View {

   

    @State var interstitial: GADInterstitialAd!

   

    var body: some View {

       

        Button(action: {

            let root = UIApplication.shared.windows.first?.rootViewController

            self.interstitial.present(fromRootViewController: root!)

        }){

            Text("Interstitial")

        }

 

        .onAppear(perform: {

            

            let request = GADRequest()

            GADInterstitialAd.load(withAdUnitID:"ca-app-pub-3940256099942544/4411468910",                                   

                request: request,

                completionHandler: { [self] ad, error in

                    if let error = error {

                        print("Failed to load interstitial ad with error: \  

                            (error.localizedDescription)")

                        return 

                     }

                     interstitial = ad

                 }

            )

        })

    }

}

これでいいのかと不安になるくらい簡単な実装ですが一応テスト広告は表示されました。

 

リワード広告

いろんな方が載せてくださっているのを借用したりしています。ありがとうございます。

 

import GoogleMobileAds

import SwiftUI

 

 

class RewardedAdDelegate: NSObject, GADFullScreenContentDelegate, ObservableObject {

    

    static var instance = RewardedAdDelegate()

    

   

    var rewardedAd: GADRewardedAd?

 

    let isTest: Bool = true

    let adId = "本番用"

    let testId = "ca-app-pub-3940256099942544/1712485313"

 

    @Published var isReady = false

    @Published var rewarded = false

 

 

    override init() {

        super.init()

 

        let request = GADRequest()

    

        GADRewardedAd.load(withAdUnitID: isTest ? testId : adId,

                           request: request, completionHandler: { (ad, error) in

                            if let error = error {

                                print("Rewarded ad failed to load with error: \(error.localizedDescription)")

                                self.isReady = false

                                return

                            }

                            self.isReady = true

                            self.rewardedAd = ad

                            self.rewardedAd?.fullScreenContentDelegate = self

                           }

        )}

 

    // 広告を表示

    func showRewardedAd() {

        let root = UIApplication.shared.windows.first?.rootViewController

        if let ad = rewardedAd {

            ad.present(fromRootViewController: root!,

                       userDidEarnRewardHandler: {

 

                        //    ここで報酬ゲット!!

                        self.rewarded = true

 

                        print("報酬を獲得しました。")

                       })} else {

                        print("Ad wasn't ready")

                       }

    }

 

  // Tells the delegate that the rewarded ad was presented.

    func adDidPresentFullScreenContent(_ ad: GADFullScreenPresentingAd) {

       

    }

 

  // Tells the delegate that the rewarded ad was dismissed.

    func adDidDismissFullScreenContent(_ ad: GADFullScreenPresentingAd) {

        print("最後までは見なかった")

     

    }

 

  // Tells the delegate that the rewarded ad failed to present.

    func ad(_ ad: GADFullScreenPresentingAd,

            didFailToPresentFullScreenContentWithError error: Error) {

        print("Rewarded ad failed to present with error: \(error.localizedDescription).")

    }

}

 

使い方

import Foundation

import SwiftUI

 

 

struct ContentView: View {

    @ObservedObject var reward = RewardedAdDelegate.instance

 

    var body: some View {

 

        if reward.isReady {

            Button(action: {

                reward.showRewardedAd()

            }, label: {

                Text("広告表示")

            })

        }

    }

}

広告が視聴できるようになったタイミングでボタンを表示している。

showRewardedAd()でスキップできない動画が始まる。

実際はGoogleのポリシーに沿って、注意喚起してから再生する必要があります。

 

以上、よく使う3つの広告スタイルの、汚くて無責任なコードですが、とりあえずコピペでもテスト広告が出せるよう備忘録的な意味合いで掲載しました。

【SwiftUI】@AppStorageで値を保存する

@AppStorageを使うと変数の変更を永続化できます。

また、値の変更はViewの監視対象になります。

 

UserDefaultsよりもコードがスッキリします。

 

@AppStorage("key_count") var count = 0

countの初期値を設定でき、変更が保存される。

 

ただし以下の型にのみ対応しています。

  • Int
  • Double
  • String
  • Bool
  • Data

【SwiftUI】効果音を鳴らす(再生、停止、ループ)

Assets.xcassetsに入れた音声ファイルを再生するには、
AVAudioPlayerのインスタンスを作り、再生・停止などの操作を行います。
音楽再生よりも、効果音の”ポン出し”に向いてそうです。

使用例です。

import SwiftUI
import AVFoundation

struct Sound {
    
    let aVAudioPlayer: AVAudioPlayer
    
    init(_ name: String) {
        self.aVAudioPlayer = try! AVAudioPlayer(data: NSDataAsset(name: name)!.data)
    }
  
    //    頭から再生
    func play(){
        aVAudioPlayer.currentTime = 0.0
        aVAudioPlayer.play()
    }
    
    func stop(){
        aVAudioPlayer.stop()
    }
    
    //    ループ再生、stop()するまで鳴り続けます
    func playLoop(){
        aVAudioPlayer.numberOfLoops = -1
        aVAudioPlayer.play()
    }
}

ちなみに、ネットからダウンロードしたフリー素材を任意のタイミングで鳴らすアプリは、
素材の再配布に該当する場合があるのでご注意ください。