I'm trying to implement interstitial ads with iAd into my spriteKit game. My code is as follows:
class ViewController: UIViewController, ADInterstitialAdDelegate {
var interAd = ADInterstitialAd()
var interAdView: UIView!
var closeButton = UIButton.buttonWithType(UIButtonType.System) as! UIButton
override func viewDidAppear(animated: Bool) {
closeButton.frame = CGRectMake(10, 10, 20, 20)
closeButton.layer.cornerRadius = 10
closeButton.setTitle("x", forState: .Normal)
closeButton.setTitleColor(UIColor.blackColor(), forState: .Normal)
closeButton.backgroundColor = UIColor.whiteColor()
closeButton.layer.borderColor = UIColor.blackColor().CGColor
closeButton.layer.borderWidth = 1
closeButton.addTarget(self, action: "close:", forControlEvents: UIControlEvents.TouchDown)
}
func close(sender: UIButton) {
closeButton.removeFromSuperview()
interAdView.removeFromSuperview()
}
func loadAd() {
println("load ad")
interAd = ADInterstitialAd()
interAd.delegate = self
}
func interstitialAdDidLoad(interstitialAd: ADInterstitialAd!) {
println("ad did load")
interAdView = UIView()
interAdView.frame = self.view!.frame
view!.addSubview(interAdView)
interAd.presentInView(interAdView)
UIViewController.prepareInterstitialAds()
interAdView.addSubview(closeButton)
}
func interstitialAdDidUnload(interstitialAd: ADInterstitialAd!) {
}
func interstitialAd(interstitialAd: ADInterstitialAd!, didFailWithError error: NSError!) {
println("failed to receive")
println(error.localizedDescription)
closeButton.removeFromSuperview()
interAdView.removeFromSuperview()
}
This code delivers me more problems than it does results. Unfortunately I am a beginner at implementing any sort of ads into an app and I don't know how I can change any of this in my favour.
One issue I get is that output traces ad did load (as you can see thats supposed to be traced once the ad is presented) however when ad did load is traced no ad is presented. I wait for at least 3 minutes but nothing happens.
Another issue I have is this WARNING: More than 10 instances of ADBannerView or ADInterstitialView currently exist. This is a misuse of the iAd API, and ad performance will suffer as a result. This message is printed only once., but this is printed after the function to load the ad is called once or twice.
Related
Eventually, I want to incorporate SKStoreProductViewController into a project I'm working on so to start I thought I'd recreate what was posted here. The only thing is, when I click on my button in the simulator (xcode 14.2) no modal is appearing. Could it be because I need to run this on a real device or maybe because storeViewController.loadProduct is 'silently failing'? Any and all help is much appreciated!
My dummy app:
//
// ViewController.swift
// SKStoreProductViewController Demo
//
import UIKit
import StoreKit
class ViewController: UIViewController {
let testFlightAppURL = URL(string: "https://apps.apple.com/us/app/testflight/id899247664")!
let testFlightProductID = 899247664
private let button: UIButton = {
let button = UIButton()
button.backgroundColor = .darkGray
button.setTitle("Open App Store", for: .normal)
return button
}()
override func viewDidLoad() {
view.addSubview(button)
button.addTarget(self, action: #selector(didTapButton), for: .touchUpInside)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
button.frame = CGRect(x: 30,
y: view.frame.height-150-view.safeAreaInsets.bottom,
width: view.frame.size.width-60,
height: 55)
}
#objc func didTapButton() {
openAppStore()
}
func openAppStore() {
// 2. Create an SKStoreProductViewController instance and set its delegate
let storeViewController = SKStoreProductViewController()
storeViewController.delegate = self
// 3. Indicate a specific product by passing its iTunes item identifier
let parameters = [SKStoreProductParameterITunesItemIdentifier: testFlightProductID]
storeViewController.loadProduct(withParameters: parameters) { _, error in
if error != nil {
// In case there is an issue loading the product, open the URL directly
UIApplication.shared.open(self.testFlightAppURL)
} else {
self.present(storeViewController, animated: true)
}
}
}
}
// MARK: - SKStoreProductViewControllerDelegate
// 4. A simple implementation of SKStoreProductViewController.
// Delegate dismisses the view controller when the user completes the purchase.
extension ViewController: SKStoreProductViewControllerDelegate {
func productViewControllerDidFinish(_ viewController: SKStoreProductViewController) {
viewController.dismiss(animated: true)
}
}
Testing on a real device resolved the issue for me.
Okay, I've spent days now doing this. I've gone through admob's tutorials, youtube, stack exchange, everything and I cannot find a solution to my problem.
I'm trying to implement admob's interstitial ads into my iOS game application. The code compiles, builds, runs, the game is played, but ads will not show up. This is my GameViewController.swift
import UIKit
import SpriteKit
import GoogleMobileAds
class GameViewController: UIViewController, GADInterstitialDelegate {
//AD STUFF-----------------------------vBELOWv----------------------------------------------
//AD STUFF-----------------------------vBELOWv----------------------------------------------
var interstitialAd: GADInterstitial?
func createInterstitialAd() -> GADInterstitial {
let request = GADRequest()
let interstitial = GADInterstitial(adUnitID: "ca-app-pub-******")
request.testDevices = [kGADSimulatorID, "C966DEAC-A2FD-4FBA-80B5-802B7E394C6D", "F3E1897A-3556-4704-9C85-3D70B4672F44","090DCEE5-6B1C-4DFB-83CE-FE800A242008", "1B93E43B-E9B3-4482-8B11-4F3614A26EA2"]
interstitial.delegate = self
interstitial.loadRequest(request)
return interstitial
}
//func to show the ad
func showAd() {
if interstitialAd != nil {
if interstitialAd!.isReady{
interstitialAd?.presentFromRootViewController(self)
}
else {print("found not ready")}
}
}
//func to pre-load new ad - calls automatically when closes an ad
func interstitialWillDismissScreen(ad: GADInterstitial!) {
interstitialAd = createInterstitialAd()
}
//AD STUFF--------------------------^ABOVE^--------------------------------------------------
//AD STUFF--------------------------^ABOVE^--------------------------------------------------
override func viewDidLoad() {
super.viewDidLoad()
interstitialAd = createInterstitialAd()
if let scene = GameScene(fileNamed:"GameScene") {
// Configure the view.
let skView = self.view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .AspectFill
scene.size = self.view.bounds.size
skView.presentScene(scene)
}
}
This is how I'm calling it to my GameScene
//makes the restart button appear
func createRestartBTN(){
restartBTN = SKSpriteNode(color: SKColor.clearColor(), size: CGSize(width: 200, height: 100))
restartBTN.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
restartBTN.zPosition = 10
addChild(restartBTN)
createReplayText()
varForGVC.showAd()
}
So when the game is over, a function makes a restart button appear. Then I want my interstitial ad to pop up so the user has to exit the interstitial ad to hit the replay button. That variable varForGVC is an implemented earlier in the GameScene.swift by varForGVC = GameViewController()
I have my app synced with Firebase and admob. I have the GoogleService-Info.plist in my root. I have the list of required frameworks for admob in my root as well. I have pod installed with Firebase. I've tried putting the code I'm using in my GameViewController into my GameScene.swift and it did not work there either. I don't know if this matters, but my game has a Main Screen, a Rules screen only accessible via the main screen, then the user hits a Play button to begin playing the game in another Screen (the GameScene). My check for .isReady does not return found not ready. I need some help with this issue. For now, I'm going to try to reinstall the frameworks and see if that's it.
This is how I work with interstitial ads from AdMob - using extension. I think it's way more clean solution. Hope it will help you!
GameViewController.swift
class GameViewController: UIViewController {
var interstitial: GADInterstitial?
override func viewDidLoad() {
createInterstitial()
}
func presentInterstitial() {
guard let interstitial = self.interstitial where interstitial.isReady else {
return
}
dispatch_async(dispatch_get_main_queue(), {
interstitial.presentFromRootViewController(self)
})
}
}
GameViewController+GADInterstitialDelegate.swift
import GoogleMobileAds
extension GameViewController: GADInterstitialDelegate {
func interstitial(ad: GADInterstitial!, didFailToReceiveAdWithError error: GADRequestError!) {
print("\(#function): \(error.localizedDescription)")
}
func interstitialDidDismissScreen(ad: GADInterstitial!) {
// Recycle interstitial
createInterstitial()
unpauseGame() // Some method from GameViewController
}
func interstitialWillPresentScreen(ad: GADInterstitial!) {
pauseGame() // Some method from GameViewController
}
func createInterstitial() {
interstitial = GADInterstitial(adUnitID: interstitialAdUnitID)
interstitial!.loadRequest(AdMobHelper.createRequest())
interstitial!.delegate = self
}
}
And the last part - AdMobHelper.swift
import GoogleMobileAds
let interstitialAdUnitID = "Some Id"
class AdMobHelper {
static func createRequest() -> GADRequest {
let request = GADRequest()
request.testDevices = ["Check this in your logs"]
return request
}
}
I figured it out finally. I used NSNotifcationCenter. I put in an observer in my GameViewController.swift in my viewDidLoad function, then a receiver where I wanted it to pop up.
I'm using Swift and SpriteKit, and everything is made inside my GameViewController and GameScene.
This is all code concerning the ads inside my GameViewController:
class GameViewController: UIViewController, ADBannerViewDelegate, GADBannerViewDelegate {
var adBannerView: ADBannerView!
var gadBannerView: GADBannerView!
var bannerDisplayed = false
var bannerNow = "iAd"
override func viewDidLoad() {
super.viewDidLoad()
if let scene = GameScene.unarchiveFromFile("GameScene") as? GameScene {
// Configure the view.
let skView = self.view as! SKView
skView.showsFPS = false
skView.showsNodeCount = false
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .AspectFill
if((UIDevice.currentDevice().userInterfaceIdiom == .Phone) && (UIScreen.mainScreen().bounds.height <= 480)) {
println("<= iPhone 4S")
scene.size.height = skView.bounds.size.height * 2
scene.size.width = skView.bounds.size.width * 2
}
NSNotificationCenter.defaultCenter().addObserver(self, selector: "hideADBanner", name: "hideAd", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "showADBanner", name: "showAd", object: nil)
self.adBannerView = self.loadAds()
skView.presentScene(scene)
println(gadBannerView)
}
}
func loadAds()->ADBannerView{
var adBannerView2 = ADBannerView(frame: CGRect.zeroRect)
adBannerView2.center = CGPoint(x: adBannerView2.center.x, y: view!.bounds.size.height - adBannerView2.frame.size.height / 2)
adBannerView2.delegate = self
adBannerView2.hidden = true
bannerNow = "iAd"
self.view?.addSubview(adBannerView2)
return adBannerView2
}
func bannerViewActionShouldBegin(banner: ADBannerView!, willLeaveApplication willLeave: Bool) -> Bool {
println("left for ad")
return true
}
func bannerViewActionDidFinish(banner: ADBannerView!) {
}
func bannerViewDidLoadAd(banner: ADBannerView!) {
println("ad did load.")
println("Delegate: \(adBannerView.delegate)")
adBannerView.hidden = false
//gadBannerView?.removeFromSuperview()
}
func bannerView(banner: ADBannerView!, didFailToReceiveAdWithError error: NSError!) {
//adBannerView.removeFromSuperview()
//self.gadBannerView = self.createGADBanner()
}
func createGADBanner()->GADBannerView {
var ad = GADBannerView()
ad = GADBannerView(frame: CGRectMake(0, 0, self.view.frame.size.width, 50))
ad.delegate = self
ad.rootViewController = self
ad.adUnitID = "xxxxxxxxxxx"
bannerNow = "GAD"
var reqAd = GADRequest()
//reqAd.testDevices = [GAD_SIMULATOR_ID] // If you want test ad's
ad.loadRequest(reqAd)
self.view.addSubview(ad)
println(ad)
return ad
}
func adViewDidReceiveAd(view: GADBannerView!) {
println("adViewDidReceiveAd:\(view)");
bannerDisplayed = true
relayoutViews()
}
func adView(bannerView: GADBannerView!, didFailToReceiveAdWithError error: GADRequestError!) {
gadBannerView.removeFromSuperview()
}
func relayoutViews() {
if (bannerDisplayed) {
var bannerFrame = gadBannerView!.frame
bannerFrame.origin.x = 0
bannerFrame.origin.y = self.view.bounds.size.height - bannerFrame.size.height
gadBannerView!.frame = bannerFrame
}
}
func hideADBanner() {
println(self.gadBannerView)
println(bannerNow)
//adBannerView?.hidden = true
//gadBannerView?.hidden = true
adBannerView?.removeFromSuperview()
gadBannerView?.removeFromSuperview()
}
func showADBanner() {
self.adBannerView = self.loadAds()
//adBannerView?.hidden = false
//gadBannerView?.hidden = false
}
So my plan is to get ads from AdMob when iAd is not working, but the problem is that my iAd "always works", even though I've changed the fill-rate inside the Developer tools in my iPhone.
When iAd loads the ad it works fine, the function bannerViewDidLoadAd() is called and the ad is displayed. Although sometimes when I load iAd it says that I've entered bannerViewDidLoadAd() but it only displays a white "box" with the iAd logo in the bottom. Now, this is not what I want because then AdMob is never gonna show. The println always put out the same delegate so it seems like that works at least.
This view never refreshes because everything, as I said, is handled in the GameScene where it's just removing and adding nodes.
Thank you!
You're over complicating things immensely with bool's, unnecessary functions, and it looks like you've actually created two ADBannerViews once in Interface Builder, self.adBannerView, and once programmatically, var adBannerView: ADBannerView!.
I've created an example with comments to help you along the way. Whats happening here is we are creating an ADBannerView, setting it up once in our viewDidLoad, and then hiding/showing our ADBannerView depending on if it receives an ad from the iAd network or not. I've left out the AdMob implementation as it should be pretty straight forward now.
import UIKit
import iAd // Import iAd
class ViewController: UIViewController, ADBannerViewDelegate { // Include the delegate for our ADBannerView
var adBannerView = ADBannerView() // Create our ADBannerView
// Create your GADBannerView here
override func viewDidLoad() {
super.viewDidLoad()
loadAds()
}
func loadAds() {
// Setup our ADBannerView
adBannerView = ADBannerView(frame: CGRect.zeroRect)
adBannerView.center = CGPoint(x: view.bounds.size.width / 2, y: view.bounds.size.height - adBannerView.frame.size.height / 2)
adBannerView.delegate = self
adBannerView.hidden = true
view.addSubview(adBannerView)
// Setup your GADBannerView here in the same manner
}
func bannerViewDidLoadAd(banner: ADBannerView!) {
println("bannerViewDidLoadAd")
// We received an ad from iAd. Lets show our banner
adBannerView.hidden = false
// Hide your GADBannerView here
}
func bannerViewActionDidFinish(banner: ADBannerView!) {
println("bannerViewActionDidFinish")
}
func bannerView(banner: ADBannerView!, didFailToReceiveAdWithError error: NSError!) {
println("didFailToReceiveAdWithError: \(error)")
// We failed to receive an ad from iAd. Lets hide our banner and print the error
adBannerView.hidden = true
// Show your GADBannerView here
}
I'm trying to add ads into my game, every time the user loses I present my game over view controller. On occasions, when an ad loads I have a full screen interstitial is shown on top of the game over screen.
My problem is the interstitial doesn't come with a close button, so I added one so the user doesn't feel forced to tap on the ad every time it comes up. When the user clicks the close button, the ad is dismissed and the game over view controller is once again shown. Problem is, once in a while (at random, maybe the first time or after a couple of runs through) the ad is dismissed and leaves a black screen.
Any ideas why this is happening? I've been scratching my head for a few days trying to figure this out.
Thanks!! Here is my code:
// Ad variables
var interstitialAdView: UIView = UIView()
var interstitial:ADInterstitialAd!
var intersitialTracker = false
var closeButton:UIButton!
override func viewDidAppear(animated: Bool) {
if !intersitialTracker {
loadInterstitialAd()
}
}
// Buttons to restart game, and return to home screen
#IBAction func restartButtonPressed(sender: AnyObject) {
intersitialTracker = false
interstitial = nil
delegate?.gameOverViewControllerDidPressRestart(self)
}
#IBAction func homeButtonPressed(sender: AnyObject) {
interstitial = nil
delegate?.gameOverViewControllerDidPressHomebutton(self)
}
func loadInterstitialAd() {
if interstitial != nil {
interstitial.delegate = nil
}
interstitial = ADInterstitialAd()
interstitial.delegate = self
intersitialTracker = true
}
func interstitialAdWillLoad(interstitialAd: ADInterstitialAd!) {
}
func interstitialAdDidLoad(interstitialAd: ADInterstitialAd!) {
interstitialAdView = UIView()
interstitialAdView.frame = self.view.bounds
view.addSubview(interstitialAdView)
closeButton = UIButton(frame: CGRect(x: 20, y: 20, width: 20, height:20))
closeButton.setBackgroundImage(UIImage(named: "close"), forState: UIControlState.Normal)
closeButton.addTarget(self, action: Selector("close"), forControlEvents: UIControlEvents.TouchDown)
self.view.addSubview(closeButton)
interstitialAd.presentInView(interstitialAdView)
}
// Called when user leaves the ad
func interstitialAdActionDidFinish(interstitialAd: ADInterstitialAd!) {
interstitialAdView.removeFromSuperview()
closeButton.removeFromSuperview()
interstitial = nil
closeButton = nil
}
// Called when user goes in ad
func interstitialAdActionShouldBegin(interstitialAd: ADInterstitialAd!, willLeaveApplication willLeave: Bool) -> Bool {
return true
}
func interstitialAd(interstitialAd: ADInterstitialAd!, didFailWithError error: NSError!) {
}
func interstitialAdDidUnload(interstitialAd: ADInterstitialAd!) {
interstitialAdView.removeFromSuperview()
closeButton.removeFromSuperview()
interstitial = nil
closeButton = nil
}
// Close button for ads
func close() {
if closeButton != nil {
interstitialAdView.removeFromSuperview()
closeButton.removeFromSuperview()
interstitial = nil
closeButton = nil
}
}
Sorry for the lengthy post, thanks!!!!!
Not entirely sure why this is happening to you but one way to work around it is to call the function that shows the game over screen. I can't tell exactly which line of code does by the above code you have given but it could look something like this;
func close() {
if closeButton != nil {
interstitialAdView.removeFromSuperview()
closeButton.removeFromSuperview()
interstitial = nil
closeButton = nil
self.GameOverScreen()
}
Had the same problem, where it would show a black screen after closing the ad.
I ended up using a UIViewController to contain the ad, and then dismissed the view controller upon the ad finishing. This is much cleaner and gets you the presenting transition for free.
Here's what the code looks like to present the ad:
- (void)interstitialAdDidLoad:(ADInterstitialAd *)interstitialAd
{
UIViewController *controller = [[UIViewController alloc] init];
[self.iadInterstitial presentInView:controller.view];
// Nearly everyone has this logic in their UIViewController. So you may
// have to replace 'self.viewController' with just 'self'.
[self.viewController presentViewController:controller animated:YES completion:nil];
}
Here's what the code looks like to dismiss:
- (void)interstitialAdActionDidFinish:(ADInterstitialAd *)interstitialAd
{
[self.viewController dismissViewControllerAnimated:YES completion:^{
self.iadInterstitial.delegate = nil;
self.iadInterstitial = nil;
}];
}
I figured it out after some time, I removed all statements where interstitial was assigned nil, instead I just did it when I exited the view controller. The screen stopped getting black after that. No sure why this worked, but it did. If someone wants to shine some light it will be welcomed!
my app Dotcha! is now on the app store and I am trying to make an update in xcode. In my update I fix some minor things and a few major things. My problem is that I screwed around with the Interstitial ads that I had working and now they don't seem to work anymore. Here is my code.
func Close(sender:UIButton){
closeButton.removeFromSuperview()
InterAdView.removeFromSuperview()
}
func loadAd() {
println("load ad")
InterAd = ADInterstitialAd()
InterAd.delegate = self
}
func interstitiallAdDidLoad(interstitialAd: ADInterstitialAd!) {
println("ad did load")
InterAdView = UIView()
InterAdView.frame = self.view.bounds
view.addSubview(InterAdView)
InterAd.presentInView(InterAdView)
UIViewController.prepareInterstitialAds()
InterAdView.addSubview(closeButton)
}
func interstitialAdDidUnload(interstitialAd: ADInterstitialAd!) {
println("Ad Unloaded")
InterAdView.removeFromSuperview()
closeButton.removeFromSuperview()
}
func interstitialAd(interstitialAd: ADInterstitialAd!, didFailWithError error: NSError!) {
println("failed to receive")
println(error.localizedDescription)
closeButton.removeFromSuperview()
InterAdView.removeFromSuperview()
}
func RandomInterstitialAd(){
var RandomAd = arc4random() % 6
switch(RandomAd){
case 0:
break
case 1:
loadAd()
break
case 2:
break
case 3:
loadAd()
break
case 4:
break
case 5:
break
default:
break
}
Close button code in viewDidLoad:
closeButton.frame = CGRectMake(20, 20, 30, 30)
closeButton.layer.cornerRadius = 10
closeButton.setTitle("X", forState: .Normal)
closeButton.setTitleColor(UIColor.blackColor(), forState: .Normal)
closeButton.backgroundColor = UIColor.whiteColor()
closeButton.layer.borderColor = UIColor.blackColor().CGColor
closeButton.layer.borderWidth = 1
closeButton.addTarget(self, action: "Close:", forControlEvents: UIControlEvents.TouchDown)
}
var InterAd = ADInterstitialAd()
var InterAdView: UIView = UIView()
var closeButton = UIButton.buttonWithType(UIButtonType.System) as! UIButton
I call the RandomInterstitialAd() function when the a button is pressed. The console prints in load Ad, but the ad never loads. Please help.