How to run function multiple times in xcode? - swift

I want to show my interstitial ad after the player have died. Meaning the interstitial ad will load in my game over scene. But i can't just write the function name in the viewdidload section. So is there a way for me to run my interstitialAd function when the player gets to the gameOverScene?
If there was an update function, i would just make a boolean value like so:
var interstitialAdShow = false
and then write this in my didmovetoview:
interstitalAdShow = true
and then in my update function:
If interstitailAdShow == true{
interstitial.present(fromRootViewController: self)
}
But now when there is no update function in the GameViewController, and i can't do it inside of my gameOverScene.swift i can't use this solution, is there another way for me to trigger my interstitial ad function when the gameOverScene comes up.
BTW here is my codes
import UIKit
import SpriteKit
import StoreKit
import GameplayKit
import GoogleMobileAds
var reklameNummer = 0
class GameViewController: UIViewController, GADBannerViewDelegate, GADRewardBasedVideoAdDelegate {
var rewardBaseAd: GADRewardBasedVideoAd!
var interstitial: GADInterstitial!
#IBOutlet weak var bannerView: GADBannerView!
override func viewDidLoad() {
super.viewDidLoad()
let request2 = GADRequest()
request2.testDevices = [kGADSimulatorID]
bannerView.delegate = self
bannerView.adUnitID = "ca-app-pub-1110799225910030/5762940412"
bannerView.rootViewController = self
//bannerView.load(request2)
interstitial = GADInterstitial(adUnitID: "ca-app-pub-1110799225910030/7460037600")
let request = GADRequest()
interstitial.load(request)
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "MenuScene") {
runTheInterStitialAd()
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
// Present the scene
view.presentScene(scene)
rewardBaseAd = GADRewardBasedVideoAd.sharedInstance()
rewardBaseAd.delegate = self
//rewardBaseAd.load(GADRequest(), withAdUnitID: "ca-app-pub-1110799225910030/4904503594")
}
view.ignoresSiblingOrder = true
view.showsFPS = false
view.showsNodeCount = false
}
}
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .allButUpsideDown
} 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
}
//MARK: Video ad
func rewardBasedVideoAd(_ rewardBasedVideoAd: GADRewardBasedVideoAd,
didRewardUserWith reward: GADAdReward) {
}
func rewardBasedVideoAdDidReceive(_ rewardBasedVideoAd:GADRewardBasedVideoAd) {
//print.text?.append("Reward based video ad is received.\n")
}
func rewardBasedVideoAdDidOpen(_ rewardBasedVideoAd: GADRewardBasedVideoAd) {
//print.text?.append("Opened reward based video ad.\n")
}
func rewardBasedVideoAdDidStartPlaying(_ rewardBasedVideoAd: GADRewardBasedVideoAd) {
//print.text?.append("Reward based video ad started playing.\n")
}
func rewardBasedVideoAdDidClose(_ rewardBasedVideoAd: GADRewardBasedVideoAd) {
}
func rewardBasedVideoAdWillLeaveApplication(_ rewardBasedVideoAd: GADRewardBasedVideoAd) {
//print.text?.append("Reward based video ad will leave application.\n")
}
func rewardBasedVideoAd(_ rewardBasedVideoAd: GADRewardBasedVideoAd,
didFailToLoadWithError error: Error) {
//print.text?.append("Reward based video ad failed to load.\n")
}
//MARK: Interstitial ad
func runTheInterStitialAd(){
var runFunc = SKAction.run(showInterstitialAdInScene)
var wait = SKAction.wait(forDuration: 1)
var sequence = SKAction.sequence([wait, runFunc])
}
func showInterstitialAdInScene() {
print("this code is working")
if var scene = SKScene(fileNamed: "Gameoverscene") {
// TRUE
if (interstitial.isReady) == true{
interstitial.present(fromRootViewController: self)
gameNumber = 0
}
// FALSE
if (interstitial.isReady) == false{
interstitial = GADInterstitial(adUnitID: "ca-app-pub-3940256099942544/1033173712")
let request = GADRequest()
interstitial.load(request)
}
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if rewardBaseAd.isReady{
if reklameNummer == 1{
reklameNummer += 1
//rewardBaseAd.present(fromRootViewController: self)
//rewardBaseAd.load(GADRequest(), withAdUnitID: "ca-app-pub-1110799225910030/4904503594")
}
}
if rewardBaseAd.isReady{
if reklameNummer == 2{
reklameNummer = 0
//rewardBaseAd.present(fromRootViewController: self)
//rewardBaseAd.load(GADRequest(), withAdUnitID: "ca-app-pub-1110799225910030/4904503594")
}
}
if gameNumber == 2{
//showInterstitialAdInScene()
}
}
}

One way to do this is use a callback, which is a way to send the class that created a new class(scene) to that new class(scene) as an argument. This way the new class(scene) can call functions from the old class. Beneath is how to implement this in your code. First I make a custom initialiser which takes in the creating class as an argument and assigns it to a local variable(myCreator). Then in the function called when a player dies(deathFunction()), I call the runIntersitialAd() function from the creating class. I also changed the code which sets up the MenuScene from the GameViewController to send itself(GameViewController class) as an argument inside the MenuScene initialisation.
Change your scene class to something like this:
class MenuScene {
var myCreator : GameViewController!
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
convenience init(fileNamed: String, VCwhoCreatedMe: GameViewController) {
self.init(fileNamed: fileNamed, VCwhoCreatedMe: VCwhoCreatedMe)
myCreator = VCwhoCreatedMe
}
....your other code
deathFunction() { //whatever code you use when the player dies
myCreator.runTheInterStitialAd() //because of the callback construction this is now possible.
...other code
}
}
Inside your GameViewController, function viewDidLoad():
if let view = self.view as! SKView? {
if let scene = MenuScene(fileNamed: "MenuScene",VCwhoCreatedMe: self ) {
runTheInterStitialAd()
scene.scaleMode = .aspectFill
view.presentScene(scene)
..... other code
}
}

Related

End or Dismiss Lottie Animation View if playOnce is finished in Swift

I already tried both loadingview.removeFromSuperView and loadingView.isHidden = true
Yes, it removes or hides the view, but I can't click on my root view anymore.
I also tried animatonview.background = .forceFinish, but doesn't do the job.
import UIKit
import Lottie
class LoadingAnimationView: UIView {
#IBOutlet weak var loadingView: UIView!
let animationView = AnimationView()
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func awakeFromNib() {
super.awakeFromNib()
}
func loadAnimation() {
let animation = Animation.named("success")
animationView.animation = animation
animationView.contentMode = .scaleAspectFill
loadingView.addSubview(animationView)
animationView.backgroundBehavior = .pauseAndRestore
animationView.translatesAutoresizingMaskIntoConstraints = false
animationView.topAnchor.constraint(equalTo: loadingView.layoutMarginsGuide.topAnchor).isActive = true
animationView.leadingAnchor.constraint(equalTo: loadingView.leadingAnchor, constant: 0).isActive = true
animationView.bottomAnchor.constraint(equalTo: loadingView.bottomAnchor).isActive = true
animationView.trailingAnchor.constraint(equalTo: loadingView.trailingAnchor, constant:0).isActive = true
animationView.setContentCompressionResistancePriority(.fittingSizeLevel, for: .horizontal)
animationView.play(fromProgress: 0,
toProgress: 1,
loopMode: .playOnce,
completion: { (finished) in
if finished {
print("Animation Complete")
//please put solution here? dismiss or end loadingView or animationView
} else {
print("Animation cancelled")
}
})
}
EDIT 2:
I'm using the loadingView when the success message is received or 200.
func goOnlineMode(){
APIManager.sharedInstance.fetchServerStatus(completion: { data, error in
if error != nil{
print("Connection Failed")
} else {
if data?.status == 200 || data?.msg == "success" {
print("connected")
loadAnimation(true)
self.setCloudStateValue(value: true)
self.vc.cloudstateChecker()
} else {
print("fail to connect")
}
}
})
}
}
this is my function loading boolean in loadAnimation for Loading the xib.
func loadAnimation(_ display: Bool) {
if (display) {
let window = UIApplication.shared.keyWindow!
if Singleton.animationView == nil {
if let view = Bundle.main.loadNibNamed("LoadingAnimationView", owner: window, options:nil)![0] as? LoadingAnimationView {
Singleton.animationView = view
Singleton.animationView?.frame.size = CGSize(width: window.bounds.width, height: window.bounds.height)
window.addSubview(Singleton.animationView!)
window.layoutIfNeeded()
Singleton.animationView?.loadAnimation()
Singleton.animationView?.translatesAutoresizingMaskIntoConstraints = false
Singleton.animationView?.leftAnchor.constraint(equalTo: window.leftAnchor).isActive = true
Singleton.animationView?.rightAnchor.constraint(equalTo: window.rightAnchor).isActive = true
Singleton.animationView?.topAnchor.constraint(equalTo: window.topAnchor, constant:-60).isActive = true
Singleton.animationView?.bottomAnchor.constraint(equalTo: window.bottomAnchor).isActive = true
window.layoutIfNeeded()
}
}
} else {
if (Singleton.animationView != nil) {
Singleton.animationView?.removeFromSuperview()
Singleton.animationView = nil
}
}
}
Try with this:
Swift 5
animationView.play { (finished) in
animationViewNewOrder!.isHidden = true
}
I solved my problem by using NotificationCenter
Swift 4.2
Add this NotificationCenter Observer in your MainViewController, and also register a Notification.Name to your Constants
NotificationCenter.default.addObserver(self, selector: #selector(removeAnimation(notification:)), name: HIDE_ANIMATION, object: nil)
}
also add this together with your observer
#objc func removeAnimation(notification:NSNotification) {
loadingAnimation(false)
}
I put this Notification Post in my newly created hideAnimation function in LoadingAnimationView.
func hideAnimation() {
NotificationCenter.default.post(name: Notification.Name(HIDE_ANIMATION.rawValue), object: nil)
loadingView.removeFromSuperview()
}
and put the hideAnimation function to your completion finish.

RealityKit – updateHandler of trackedRaycast not called

I call trackedRaycast() from startTracing() which is called from viewDidLoad(). My goal is to have the AR content be placed wherever the raycast from the camera hits a horizontal surface and that it updates when the raycast intersects a different location. When the user taps the screen the updates stop.
The updateHandler closure of the function trackedRaycast is never executed.
import RealityKit
import ARKit
class ViewController: UIViewController {
#IBOutlet var arView: ARView!
var gameAnchor: Experience.PreviewBoard!
var raycast: ARTrackedRaycast?
var gameIsPlaced = false
override func viewDidLoad() {
super.viewDidLoad()
setupARConfiguration()
loadGameBoard()
startTracing()
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap(recognizer:)))
arView.addGestureRecognizer(tapGestureRecognizer)
}
func setupARConfiguration() {
arView.automaticallyConfigureSession = false
let config = ARWorldTrackingConfiguration()
config.planeDetection = [.horizontal]
config.isCollaborationEnabled = true
config.environmentTexturing = .automatic
arView.session.run(config)
}
func loadGameBoard() {
gameAnchor = try! Experience.loadPreviewBoard()
arView.scene.addAnchor(gameAnchor)
}
func startTracing() {
raycast = arView.trackedRaycast(from: view.center, allowing: .estimatedPlane, alignment: .horizontal) { results in
print("This is never executed")
// Refine the game position with raycast update
if let result = results.first {
self.gameAnchor.setTransformMatrix(result.worldTransform, relativeTo: nil)
}
}
}
func stopTracing() {
print("raycast has stopped")
raycast?.stopTracking()
}
#objc func handleTap(recognizer: UITapGestureRecognizer) {
if !gameIsPlaced {
gameIsPlaced = true
stopTracing()
}
}
}
What could be the issue?
There appears to be some kind of lag when the view is initialized, leading to the tracked raycast not being initialized from the usual lifecycle methods. When I tried initializing it via a tap on the screen, it worked as intended. You can check this with the following code:
var r : ARTrackedRaycast? {
didSet {
print("Raycast initialized")
}
}
r = self.arView.trackedRaycast(from: self.view.center, allowing: .existingPlaneInfinite, alignment: .any) { result in
print("Callback received")
}
If you initialize your raycast from viewDidLoad() or viewDidAppear() even, the setter is never called.
The solution for me was to delay the raycast's initialization by a few seconds, like so:
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
r = self.arView.trackedRaycast(from: self.view.center, allowing: .existingPlaneInfinite, alignment: .any) { result in
print("Callback received")
}
This is, obviously, far from ideal, but right now I'm not sure how to go about solving that issue any differently.

How to position SKTilemap Scene in the middle of screen in Swift

I have created a TilemapScene (CKTilemapScene.sks) in Swift 5. This is basically to lay a tile map background of my game project. Refer to the second screenshot below.
Then, in my main swift code, I load all the background with the standard codes.
But, somehow the tile map is not centered. Refer to the first screenshot below . But, at the bottom left of the screen. I tried to play with anchor point. That doesn't help. Did I set anything wrong?
import UIKit
import SpriteKit
import GameplayKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
if let scene = SKScene(fileNamed: "CKTilemapScene") {
// Present the scene
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
view.showsPhysics = true
}
}
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .landscape
//.allButUpsideDown
} else {
return .all
}
}
Set the position of SKScene to the SKView's center.
override func viewDidLoad() {
super.viewDidLoad()
if let view = view as? SKView {
if let scene = SKScene(fileNamed: "CKTilemapScene") {
scene.position = view.center
view.presentScene(scene)
}
}
//...
}

SKLabelNode Won't Update Text From Function

I have this game project and it has a coin system. I also have an option for users to watch a video ad to get 50 coins added to their total. Since the shop page is a SKScene, I run the ad through the view controller and when it is done presenting, the viewcontroller runs a function inside the scene that should update the amount of coins and the label that displays them.
Here is the code for my view controller:
class GameViewController: UIViewController , GADRewardBasedVideoAdDelegate {
var rewardBasedVideo: GADRewardBasedVideoAd!
var rewardvideoisinprogress = false
var interstitial: GADInterstitial!
var adcounter = 0
override func viewDidLoad() {
super.viewDidLoad()
adcounter = UserDefaults.standard.integer(forKey: "AdCounter")
print(adcounter)
createandloadvideoad()
interstitial = GADInterstitial(adUnitID: "ca-app-pub-3343126174384559/7308554354")
let request = GADRequest()
interstitial.load(request)
ThemeShop().updatecoins()
NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.presentvideoad), name: NSNotification.Name("video"), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.inter), name: NSNotification.Name("inter_"), object: nil)
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "MainMenu") {
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
// Present the scene
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
}
}
#objc func inter(){
adcounter = adcounter + 1
print(adcounter)
if adcounter == 2{
adcounter = 0
if interstitial.isReady {
interstitial.present(fromRootViewController: self)
interstitial = createad()
} else {
print("Ad wasn't ready")
}
}
UserDefaults.standard.set(adcounter, forKey: "AdCounter")
}
func createad() -> GADInterstitial {
let inter = GADInterstitial(adUnitID: "ca-app-pub-3343126174384559/7308554354`")
inter.load(GADRequest())
return inter
}
func createandloadvideoad(){
rewardBasedVideo = GADRewardBasedVideoAd.sharedInstance()
rewardBasedVideo?.delegate = self
if !rewardvideoisinprogress && rewardBasedVideo?.isReady == false{
//ca-app-pub-3343126174384559/3425197396
rewardBasedVideo?.load(GADRequest(), withAdUnitID: "ca-app-pub-3940256099942544/1712485313")
rewardvideoisinprogress = true
}
}
#objc func presentvideoad(){
if rewardBasedVideo?.isReady == true{
rewardBasedVideo?.present(fromRootViewController: self)
}else{
print("Was NOt Reardyadjfsjfsalfkj")
}
createandloadvideoad()
}
func rewardBasedVideoAd(_ rewardBasedVideoAd: GADRewardBasedVideoAd,
didFailToLoadWithError error: Error) {
print("Reward based video ad failed to load: \(error.localizedDescription)")
rewardvideoisinprogress = false
}
func rewardBasedVideoAdDidReceive(_ rewardBasedVideoAd: GADRewardBasedVideoAd) {
print("Reward based video ad is received.")
}
func rewardBasedVideoAdDidOpen(_ rewardBasedVideoAd: GADRewardBasedVideoAd) {
print("Opened reward based video ad.")
}
func rewardBasedVideoAdDidStartPlaying(_ rewardBasedVideoAd: GADRewardBasedVideoAd) {
print("Reward based video ad started playing.")
}
func rewardBasedVideoAdDidClose(_ rewardBasedVideoAd: GADRewardBasedVideoAd) {
rewardvideoisinprogress = false
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1500) , execute: {
print("Brikdsajfaslkd")
ThemeShop().updatecoins()
})
}
func rewardBasedVideoAd(_ rewardBasedVideoAd: GADRewardBasedVideoAd,
didRewardUserWith reward: GADAdReward) {
var coins = UserDefaults.standard.integer(forKey: "Coins")
coins = coins + 50
UserDefaults.standard.set(coins, forKey: "Coins")
}
}
This is the function inside the SKScene:
func updatecoins(){
print("Updating")
coins = UserDefaults.standard.integer(forKey: "Coins")
print("Coins: \(coins)")
self.coinlabel.text = String(self.coins)
print(self.coinlabel.text)
}
I know that the coins are being added to the balance behind the scenes because if I exit the scene and reenter, the coins show up like they should.
you're trying to call a function on a class variable of ThemeShop you need to be calling the func on an instance variable of the class.
you don't show your ViewController code so you may have to adapt this accordingly
have a scene variable at the top of your viewController
private var themeShop: ThemeShop!
then in your viewDidLoad when you load the scene put it inside of the variable
//this is if you are loading your scene from an sks file.
//your code may be different if it is just loaded from code with no sks file
if let scene = SKScene(fileNamed: "ThemeShop") {
self.themeShop = scene
then in your viewController when you need to access the scene
func rewardBasedVideoAdDidClose(_ rewardBasedVideoAd: GADRewardBasedVideoAd) {
rewardvideoisinprogress = false
sleep(1)
themeShop.updatecoins()
}
I think you label is not updating because you are not on the main queue. Also I made some changes to your code. You shouldn't use sleep() better use asyncAfter:
func rewardBasedVideoAdDidClose(_ rewardBasedVideoAd: GADRewardBasedVideoAd) {
isRewardVideoInProgress = false
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
ThemeShop().updatecoins()
}
}
func rewardBasedVideoAd(_ rewardBasedVideoAd: GADRewardBasedVideoAd,
didRewardUserWith reward: GADAdReward) {
var coins = UserDefaults.standard.integer(forKey: "Coins")
coins = coins + 50
UserDefaults.standard.set(coins, forKey: "Coins")
}
func updatecoins(){
print("Updating")
coins = UserDefaults.standard.integer(forKey: "Coins")
print("Coins: \(coins)")
DispatchQueue.main.async {
self.coinlabel.text = "\(self.coins)"
print(self.coinlabel.text)
}
}

How to put GameCenter on application with swift?

I made a game using SpriteKit and Xcode 7 beta. I tried to add GameCenter and Leaderboard but the problem is that the score in the leaderboard won't change (HighScore doesn't upload to GameCenter), It stay 0 all the time and I don't know how to fix it. I'm using 2 different files: GameViewController.swift, and PointsLabel.swift
GameViewController.swift:
import GameKit
class GameViewController: UIViewController,UIGestureRecognizerDelegate, GKGameCenterControllerDelegate {
var score: PointsLabel!
override func viewDidLoad() {
super.viewDidLoad()
//initiate gamecenter
func authenticateLocalPlayer(){
let localPlayer = GKLocalPlayer.localPlayer()
localPlayer.authenticateHandler = {(GameViewController, error) -> Void in
if (GameViewController != nil) {
self.presentViewController(GameViewController!, animated: true, completion: nil)
}
else {
print((GKLocalPlayer.localPlayer().authenticated))
}
}
}
}
#IBAction func leaderboard(sender: UIButton) {
saveHighscore(score)
showLeader()
}
//send high score to leaderboard
func saveHighscore(score:Int) {
//check if user is signed in
if GKLocalPlayer.localPlayer().authenticated {
let scoreReporter = GKScore(leaderboardIdentifier: "Leaderboard_01")
scoreReporter.value = Int64(score)
let scoreArray: [GKScore] = [scoreReporter]
GKScore.reportScores(scoreArray, withCompletionHandler: {error -> Void in
if error != nil {
print("error")
}
})
}
}
//shows leaderboard screen
func showLeader() {
let vc = self.view?.window?.rootViewController
let gc = GKGameCenterViewController()
gc.gameCenterDelegate = self
vc?.presentViewController(gc, animated: true, completion: nil)
}
}
//hides leaderboard screen
func gameCenterViewControllerDidFinish(gameCenterViewController: GKGameCenterViewController)
{
gameCenterViewController.dismissViewControllerAnimated(true, completion: nil)
}
There is an Error in this file:
Cannot invoke 'saveHighscore' with an argument list of type '(PointsLabel!)'
on code:
#IBAction func leaderboard(sender: UIButton) {
saveHighscore(score) //<- Here is Error
showLeader()
}
PointsLabel.swift:
import Foundation
import UIKit
import SpriteKit
class PointsLabel: SKLabelNode {
var score:Int = 0
init(num: Int) {
super.init()
fontColor = UIColor.blackColor()
fontSize = 30.0
score = num
text = "\(num)"
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func increment() {
score++
text = "\(score)"
}
func setTo(num: Int) {
self.score = num
text = "\(self.score)"
}
}
I don't know how to fix it!
Your score variable is of type PointsLabel but your saveHighscore function expects an Int as parameter.
Looking at your code, the variable score will be an instance of PointsLabel so I guess you could use the score property of your instanciated class, which is an Int (the fact that you used "score" as a name for both variables is confusing. I suggest changing names to make them more explicit.).
#IBAction func leaderboard(sender: UIButton) {
saveHighscore(score.score)
showLeader()
}