Explanation
I have a game that runs in landscape, and I'm trying to send an email, also, in portrait mode.
Basically:
if the actual view is GameViewController = .Landscape;
if the actual view is MailViewController = .AllButUpsideDown.
Demo code
I built this code below, but I'm having an issue with it:
Mail isn't being called when touching the screen.
You can download this code here.
GameScene
import SpriteKit
class GameScene: SKScene {
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
//Mail
NSNotificationCenter.defaultCenter().postNotificationName("openMail", object: nil)
}
}
GameViewController
import UIKit
import SpriteKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Set view size.
let scene = GameScene(size: view.bounds.size)
// Configure the view.
let skView = 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 = .ResizeFill
skView.presentScene(scene)
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
return .Landscape
} else {
return .Landscape
}
}
}
MailViewController
import UIKit
import SpriteKit
import MessageUI
class MailViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//Register mail observer (so I can call from GameScene)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(MailViewController.openMailController), name: "openMail", object: nil)
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
return .AllButUpsideDown
} else {
return .All
}
}
}
//Mail
extension MailViewController: MFMailComposeViewControllerDelegate {
func openMailController() {
let mailComposerVC = MFMailComposeViewController()
mailComposerVC.mailComposeDelegate = self //extremely important to set the mailComposeDelegate property, not the delegate property
self.presentViewController(mailComposerVC, animated: true, completion: nil)
}
func mailComposeController(controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?) {
self.dismissViewControllerAnimated(true, completion: nil)
}
}
Thanks for any help,
Luiz.
Problem with your provided code is that observer in MailViewController class view did load method is not called. So Observer is not set to receive any notification.
Also you didn't called segue anywhere in your code to call MailViewController.
Solution: By using Delegate Method
GameScene class updated:
import SpriteKit
// Protocol
protocol gameSceneDelegate {
func openMail()
}
// Protocol
class GameScene: SKScene {
// Delegate
var sceneDelegate : gameSceneDelegate?
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
//Mail
// NSNotificationCenter.defaultCenter().postNotificationName("openMail", object: nil)
// delegate method called on receiving touches
self.sceneDelegate?.openMail()
}
}
GameViewController class updated:
import UIKit
import SpriteKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Set view size.
let scene = GameScene(size: view.bounds.size)
// Delegate Confirmation
scene.sceneDelegate = self
// Configure the view.
let skView = 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 = .ResizeFill
skView.presentScene(scene)
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
return .Landscape
} else {
return .Landscape
}
}
}
// Delegate Method Implementation
extension GameViewController : gameSceneDelegate{
func openMail(){
self.performSegueWithIdentifier("openMail", sender: self);
}
}
Mail View Controller:
import UIKit
import SpriteKit
import MessageUI
class MailViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//Register mail observer (so I can call from GameScene)
// NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(MailViewController.openMailController), name: "openMail", object: nil)
self.openMailController()
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
return .AllButUpsideDown
} else {
return .All
}
}
}
//Mail
extension MailViewController: MFMailComposeViewControllerDelegate {
func openMailController() {
let mailComposerVC = MFMailComposeViewController()
mailComposerVC.mailComposeDelegate = self //extremely important to set the mailComposeDelegate property, not the delegate property
self.presentViewController(mailComposerVC, animated: true, completion: nil)
}
func mailComposeController(controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?) {
self.dismissViewControllerAnimated(true, completion: nil)
}
}
Last change in storyboard:
click segue connecting GameViewController and MailViewController and change identifier to - openMail or whatever you want to keep
Related
I have a GameViewController, which transitions between different scenes, for example: CircleScene to SquareScene, back & forth with Transitions.
All of the above logic works fine, as long as GameViewController is "initial view controller".
Once I have one other view controller, above the GameViewController, the screen transitions for GameViewController doesn't work anymore.
GameViewController.swift
import UIKit
import SpriteKit
import GameplayKit
protocol ScreenManager {
func gotoSquare()
func gotoCircle()
}
class GameViewController: UIViewController, ScreenManager {
let sceneHeight = 380
let sceneWidth = 570
func gotoCircle() {
let transition = SKTransition.push(with: .right, duration: 1.0)
let newScene = CircleScene(size: CGSize(width: sceneWidth, height: sceneHeight))
newScene.screenManager = self
newScene.scaleMode = .aspectFill
let view = self.view as! SKView?
view?.presentScene(newScene, transition: transition)
}
func gotoSquare() {
let transition = SKTransition.push(with: .left, duration: 1.0)
let newScene = SquareScene(size: CGSize(width: sceneWidth, height: sceneHeight))
newScene.screenManager = self
newScene.scaleMode = .aspectFill
let view = self.view as! SKView?
view?.presentScene(newScene, transition: transition)
}
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
let scene = CircleScene(size: CGSize(width: sceneWidth, height: sceneHeight))
scene.screenManager = self
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
// Present the scene
view.presentScene(scene)
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .landscape
} else {
return .all
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override var prefersStatusBarHidden: Bool {
return true
}
}
MainViewContoller.swift
import UIKit
class MainViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func btnPressed(_ sender: AnyObject) {
// Go to GameViewController
let gameViewController:GameViewController = storyboard?.instantiateViewController(withIdentifier: "GameViewControllerID") as! GameViewController
self.present(gameViewController, animated:false)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .landscape
} else {
return .all
}
}
override var prefersStatusBarHidden: Bool {
return true
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
I have tried ViewController methods (present/show) unsuccessfully. Is there anything wrong with ViewController presentation that causes scene transitions to fail? Appreciate your help.
How do I show an interstitial ad from admob every x times the user has died or every x times the user does something like presses a button? This is how I showed an interstitial ad on my GameScene and limited ad impressions with a simple if statement.
This will only work if you have the GoogleMobileAds.sdk and have imported the googlemobileads module into your GameViewController, and GameScene, OR GameOverScene.
I'll be showing you cross-scene ad implementation and programmatically limiting ad impressions.
First, in your GameViewController:
import GoogleMobileAds
class GameViewController: UIViewController, GADInterstitialDelegate {
var myAd = GADInterstitial()
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(GameViewController.loadAndShow), name: "loadAndShow", object: nil)
}
Create two functions at the bottom of your GameViewController:
func loadAndShow() {
myAd = GADInterstitial()
let request = GADRequest()
myAd.setAdUnitID("ca-app-pub-3940256099942544/4411468910")
myAd.delegate = self
myAd.loadRequest(request)
}
func interstitialDidReceiveAd(ad: GADInterstitial!) {
if (self.myAd.isReady) {
myAd.presentFromRootViewController(self)
}
}
You are done with GameViewController. Now head to GameOverScene or GameScene, whatever you need.
Create a global int variable:
var playCount = Int()
In your DidMoveToView say:
playCount = 1
This part is sort of confusing, kinda, not really. Go to your touchesBegan and find where you add actions to a button if it's pressed. For example, a resetGame button resets the scene. Add this there and increment the playButton Int like so:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches{
let location = touch.locationInNode(self)
if resetGame.containsPoint(location) {
restartScene()
playCount += 1
}
Last step. Add these two functions to the bottom of the scene you want to show interstitial ads in:
func displayAd() {
NSNotificationCenter.defaultCenter().postNotificationName("loadAndShow", object: nil)
}
func checkAd() {
if playCount % 4 == 0 {
displayAd()
}
}
}
Now every fourth time that the user presses the reset game button or dies, an interstitial ad should show up. I hope this helps.
EDIT: I forgot to tell you to call the checkAd() function. Call this function wherever your players dies. So if you have a Bool variable called died or gameover call it in the same spot. For example..
if died == true {
checkAd()
}
import UIKit
import SpriteKit
import GoogleMobileAds
var playCount = Int()
class GameViewController: UIViewController, GADBannerViewDelegate {
#IBOutlet var banner: GADBannerView!
var myAd = GADInterstitial()
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(GameViewController.loadAndShow), name: "loadAndShow", object: nil)
let scene = MainScene(size: CGSize(width: 1536, height: 2048))
// 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
skView.presentScene(scene)
banner.hidden = true
banner.delegate = self
banner.adUnitID = "ca-app-pub-8889875503423788/7902691359"
banner.rootViewController = self
banner.loadRequest(GADRequest())
}
func loadAndShow() {
myAd = GADInterstitial()
let request = GADRequest()
myAd.setAdUnitID("ca-app-pub-8889875503423788/7902691359")
myAd.delegate = self
myAd.loadRequest(request)
}
func interstitialDidReceiveAd(ad: GADInterstitial!) {
if (self.myAd.isReady) {
myAd.presentFromRootViewController(self)
}
}
func adViewDidReceiveAd(bannerView: GADBannerView!) {
banner.hidden = false
}
func adView(bannerView: GADBannerView!, didFailToReceiveAdWithError error: GADRequestError!) {
banner.hidden = true
}
override func shouldAutorotate() -> Bool {
return true
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
return .AllButUpsideDown
} else {
return .All
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override func prefersStatusBarHidden() -> Bool {
return true
}
}
My app was approved just a couple of days ago and had an iAd banner on the bottom of the screen. I know that there it can take up to a week for ads to start appearing, but while I'm waiting.. can someone make sure that I don't have any errors in my code?
import UIKit
import SpriteKit
import iAd
import GameKit
class GameViewController: UIViewController, ADBannerViewDelegate, GKLocalPlayerListener {
var adBanner: ADBannerView!
override func viewDidLoad() {
super.viewDidLoad()
authenticateLocalPlayer()
if let scene = GameScene(fileNamed:"GameScene") {
// Configure the view.
let skView = self.view as! SKView
skView.ignoresSiblingOrder = true
scene.scaleMode = .AspectFill
skView.presentScene(scene)
}
}
func loadBanner() {
adBanner = ADBannerView(frame: CGRect.zero)
adBanner.center = CGPoint(x: adBanner.center.x, y: view.bounds.size.height - adBanner.frame.size.height / 2)
adBanner.delegate = self
adBanner.hidden = true
view.addSubview(adBanner)
}
func bannerViewDidLoadAd(banner: ADBannerView!) {
adBanner.hidden = false
}
func bannerView(banner: ADBannerView!, didFailToReceiveAdWithError error: NSError!) {
adBanner.hidden = true
}
func bannerViewActionShouldBegin(banner: ADBannerView!, willLeaveApplication willLeave: Bool) -> Bool {
return true
}
override func shouldAutorotate() -> Bool {
return true
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
return .AllButUpsideDown
} else {
return .All
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override func prefersStatusBarHidden() -> Bool {
return true
}
func authenticateLocalPlayer() {
let localPlayer = GKLocalPlayer.localPlayer()
localPlayer.authenticateHandler = {(viewController, error) -> Void in
if (viewController != nil) {
self.presentViewController(viewController!, animated: true, completion: nil)
}
else {
print((GKLocalPlayer.localPlayer().authenticated))
}
}
}
}
Right now it just displays a white rectangle. Is there anything wrong with my code, or do I just need to keep waiting for my app to start receiving ads?
iAd is officially discontinued as of tomorrow (June 30th, 2016). I am unsure of the "sign up" process now, but I believe Apple stopped registering apps to receive ads from their network some months ago
Who calls the loadBanner()?
You might consider overriding viewDidAppear() and shifting the code there.
I currently have a bug with the app I am working on where when a user taps on an iAd banner and the ad loads, the game resets. By "resets" I mean all their progression (other than the NSUserDefaults data) is reset to 0 and the scene is reset back to the start of the game.
My game primary runs from didMoveToView() and touchesBegan(). In my GameViewController the game scene is built from viewWillLayoutSubviews().
Here is how I control my ads and placement. Not sure if has to do with the placement of my code or the ad.
import UIKit
import SpriteKit
import iAd
// Global Ad Variable
var adBanner: ADBannerView!
class GameViewController: UIViewController, ADBannerViewDelegate {
/* Load Ads */
func loadAds() {
adBanner = ADBannerView(frame: CGRect(x: 0, y: view.bounds.size.height - 50, width: 320, height: 50))
adBanner.delegate = self
adBanner.hidden = true
self.view.addSubview(adBanner)
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
if let scene = GameScene(fileNamed:"GameScene") {
// Configure the view.
let skView = self.view as! SKView
// Create and Configure the scene
scene.size = skView.bounds.size
skView.showsFPS = false // SHOW OR HIDE FRAMES PER SECOND
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
skView.presentScene(scene)
}
}
override func awakeFromNib() {
super.awakeFromNib()
loadAds()
}
override func shouldAutorotate() -> Bool {
return true
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
return .AllButUpsideDown
} else {
return .All
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override func prefersStatusBarHidden() -> Bool {
return true
}
func bannerView(banner: ADBannerView!, didFailToReceiveAdWithError error: NSError!) {
adBanner.hidden = true
print("There was an error loading ad")
}
func bannerViewWillLoadAd(banner: ADBannerView!) {
print("Ad Loading")
}
func bannerViewDidLoadAd(banner: ADBannerView!) {
adBanner.hidden = false
print("Ad Loaded")
}
}
I have imported iAd into my GameScene.swift file and for some reason I cannot call the self.canDisplayBannerAds() method. Does anyone know how to solve this?
Here is some working code for you that just worked for me. This does not need self.candisplaybannerads = true as I had some issues with that. The ad automatically changes the size according to the screen size and is located at the bottom of the screen.
import iAd
class viewController: UIViewController, ADBannerViewDelegate {
var AdBanner = ADBannerView()
override func viewDidLoad() {
super.viewDidLoad()
/* Ad Banner Settings */
AdBanner = ADBannerView()
AdBanner.frame = CGRectZero
AdBanner.delegate = self
self.AdBanner.frame = CGRectMake(0, self.view.frame.size.height-self.AdBanner.frame.size.height, self.AdBanner.frame.size.width, self.AdBanner.frame.size.height)
AdBanner.backgroundColor = UIColor.clearColor()
self.view.addSubview(AdBanner)
}
/* All iAd Functions */
func bannerViewActionShouldBegin(banner: ADBannerView!, willLeaveApplication willLeave: Bool) -> Bool {
/* whatever you need */
return true
}
func bannerViewActionDidFinish(banner: ADBannerView!) {
/* whatever you need */
}
func bannerViewDidLoadAd(banner: ADBannerView!) {
AdBanner.hidden = false
}
func bannerView(banner: ADBannerView!, didFailToReceiveAdWithError error: NSError!) {
NSLog("Error Loading Ad")
/* whatever you need */
AdBanner.hidden = true
}
func bannerViewWillLoadAd(banner: ADBannerView!) {
/* whatever you need */
}
If you want to use canDisplayBannerAds implement it like so:
import UIKit
import iAd // Import iAd
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
showBanner()
}
func showBanner() {
// Creates a self managed ADBannerView
self.canDisplayBannerAds = true
}
func hideBanner() {
// Removes the ADBannerView
self.canDisplayBannerAds = false
}
Using canDisplayBannerAds creates and manages an ADBannerView for you. There's no need to implement additional methods and the ADBannerView will hide/show itself depending on if there is an advertisement available or not.
Did you remember to inherrit from ADBannerViewDelegate AND import iAd
yourVC: UIViewController, ADBannerViewDelegate {
...
}