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