全部文章请移步:
SwiftUI APP 集成 Google 移动广告 Admob (一):https://www.xugj520.cn/archives/swiftui-admob-1.html
SwiftUI APP 集成 Google 移动广告 Admob (二):https://www.xugj520.cn/archives/swiftui-admob-2.html
简介:
随着越来越多的应用程序使用 SwiftUI 构建,我想提供一种简单的方法将 Admob 直接集成到您的应用程序中。为了让大家集成的更容易,我计划将它作为 Cocoapod 发布。
先看一下集成后的效果:
设置 Google 移动广告 (Admob) SDK
申请应用,请参考 Google 的官方文档地址为:https://developers.google.com/admob/ios/quick-start。
使用 Cocoapods 导入 SDK
pod 'Google-Mobile-Ads-SDK'
然后使用命令行运行
pod install --repo-update
更新 Info.plist
请更新应用的 Info.plist 文件以添加以下两个键:
- 一个字符串值为您的 AdMob 应用 ID 的 GADApplicationIdentifier 键(在 AdMob 界面中标识)。
- 一个 SKAdNetworkIdentifier 值为 Google(cstr6suwn9.skadnetwork),并选择向 Google 提供了这些值的其他买家的 SKAdNetworkItems 键。
<key>GADApplicationIdentifier</key>
<string>ca-app-pub-3940256099942544~2435281174</string>
初始化 Google 移动广告 (Admob) SDK
import SwiftUI
import AppTrackingTransparency
import GoogleMobileAds
@main
struct ExampleApp: App {
//在 App Delegate 中使用 init() 代替 ApplicationDidFinishLaunchWithOptions
init() {
if ATTrackingManager.trackingAuthorizationStatus == .notDetermined {
// TODO 这里可以弹出隐私协议
} else {
ATTrackingManager.requestTrackingAuthorization { status in
GADMobileAds.sharedInstance().start(completionHandler: nil)
}
}
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
横幅广告(自适应)
创建一个 BannerAd继承 UIViewController
class BannerAdVC: UIViewController {
let adUnitId: String
//Initialize variable
init(adUnitId: String) {
self.adUnitId = adUnitId
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
var bannerView: GADBannerView = GADBannerView() //Creates your BannerView
override func viewDidLoad() {
bannerView.adUnitID = adUnitId
bannerView.rootViewController = self
//Add our BannerView to the VC
view.addSubview(bannerView)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
loadBannerAd()
}
//Allows the banner to resize when transition from portrait to landscape orientation
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate { _ in
self.bannerView.isHidden = true //So banner doesn't disappear in middle of animation
} completion: { _ in
self.bannerView.isHidden = false
self.loadBannerAd()
}
}
func loadBannerAd() {
let frame = view.frame.inset(by: view.safeAreaInsets)
let viewWidth = frame.size.width
//Updates the BannerView size relative to the current safe area of device (This creates the adaptive banner)
bannerView.adSize = GADCurrentOrientationAnchoredAdaptiveBannerAdSizeWithWidth(viewWidth)
bannerView.load(GADRequest())
}
}
要在 SwiftUI 中使用新创建的UIViewController,您需要创建一个UIViewControllerRepresentable
final class BannerAd: UIViewControllerRepresentable {
let adUnitId: String
init(adUnitId: String) {
self.adUnitId = adUnitId
}
func makeUIViewController(context: Context) -> BannerAdVC {
return BannerAdVC(adUnitId: adUnitId)
}
func updateUIViewController(_ uiViewController: BannerAdVC, context: Context) {
}
}
现在你的横幅广告是一个 SwiftUI 视图,但我们仍然需要设置一些东西,以便你可以将它添加到你的应用程序中。让我们创建一个名为SwiftUIBannerAd的新 SwiftUI 视图。此视图将管理您的广告的位置和框架。
struct SwiftUIBannerAd: View {
@State var height: CGFloat = 0 //Height of ad
@State var width: CGFloat = 0 //Width of ad
@State var adPosition: AdPosition
let adUnitId: String
init(adPosition: AdPosition, adUnitId: String) {
self.adPosition = adPosition
self.adUnitId = adUnitId
}
enum AdPosition {
case top
case bottom
}
public var body: some View {
VStack {
if adPosition == .bottom {
Spacer() //Pushes ad to bottom
}
//Ad
BannerAd(adUnitId: adUnitId)
.frame(width: width, height: height, alignment: .center)
.onAppear {
//Call this in .onAppear() b/c need to load the initial frame size
//.onReceive() will not be called on initial load
setFrame()
}
//Changes the frame of the ad whenever the device is rotated.
//This is what creates the adaptive ad
.onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
setFrame()
}
if adPosition == .top {
Spacer() //Pushes ad to top
}
}
}
func setFrame() {
//Get the frame of the safe area
let safeAreaInsets = UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.safeAreaInsets ?? .zero
let frame = UIScreen.main.bounds.inset(by: safeAreaInsets)
//Use the frame to determine the size of the ad
let adSize = GADCurrentOrientationAnchoredAdaptiveBannerAdSizeWithWidth(frame.width)
//Set the ads frame
self.width = adSize.size.width
self.height = adSize.size.height
}
}
就是这样!您可以将广告添加到您的应用中。我建议您通过将其放置在ZStack的最高级别来执行此操作。(参考开头的例子)
测试您的广告时,请务必将adUnitId设置为横幅测试广告单元 ID:ca-app-pub-3940256099942544/2934735716
您可能已经注意到,我使用NotificationCenter和UIApplication来获取安全区域和方向。虽然您可以使用GeometryReader来执行此操作,但我不建议将其用于生产应用程序。在测试我自己的 SwiftUI 应用程序WidgeTube 时,我发现GeometryReader对于生产来说根本不可靠。很多时候,它是 SwiftUI 的属性图崩溃的根源。虽然有些人已经找到了避免这种情况的方法,但通过试验视图堆栈的顺序,我决定最好完全避免使用GeometryReader。
插页式广告
首先,我们需要创建一个InterstitialAdObject。此对象将处理您的应用程序中的插页式广告的加载。
class InterstitialAd: NSObject {
var interstitialAd: GADInterstitialAd?
//Want to have one instance of the ad for the entire app
//We can do this b/c you will never show more than 1 ad at once so only 1 ad needs to be loaded
static let shared = InterstitialAd()
func loadAd(withAdUnitId id: String) {
let req = GADRequest()
GADInterstitialAd.load(withAdUnitID: id, request: req) { interstitialAd, err in
if let err = err {
print("Failed to load ad with error: \(err)")
return
}
self.interstitialAd = interstitialAd
}
}
}
我们现在将使用这个对象来创建一个InterstitialAdView。该视图将是一个UIViewControllerRepresentable以及一个GADFullScreenContentDelegate。
class InterstitialAdView: NSObject, UIViewControllerRepresentable, GADFullScreenContentDelegate {
//Here's the Ad Object we just created
let interstitialAd = InterstitialAd.shared.interstitialAd
@Binding var isPresented: Bool
var adUnitId: String
init(isPresented: Binding<Bool>, adUnitId: String) {
self._isPresented = isPresented
self.adUnitId = adUnitId
super.init()
interstitialAd?.fullScreenContentDelegate = self //Set this view as the delegate for the ad
}
//Make's a SwiftUI View from a UIViewController
func makeUIViewController(context: Context) -> UIViewController {
let view = UIViewController()
//Show the ad after a slight delay to ensure the ad is loaded and ready to present
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1)) {
self.showAd(from: view)
}
return view
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
}
//Presents the ad if it can, otherwise dismisses so the user's experience is not interrupted
func showAd(from root: UIViewController) {
if let ad = interstitialAd {
ad.present(fromRootViewController: root)
} else {
print("Ad not ready")
self.isPresented.toggle()
}
}
func adDidDismissFullScreenContent(_ ad: GADFullScreenPresentingAd) {
//Prepares another ad for the next time view presented
InterstitialAd.shared.loadAd(withAdUnitId: adUnitId)
//Dismisses the view once ad dismissed
isPresented.toggle()
}
}
稍后,我们将创建全屏修改器来呈现此视图。现在,我们将保留现有内容并创建激励广告(如果您不创建激励广告,请跳至全屏修改器)。
测试时,使用插页式测试广告单元 ID:ca-app-pub-3940256099942544/4411468910