I'm trying to implement interstitial ads with iAd into my spriteKit game. My code is as follows:
class MainViewController: 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.
Any help is more than appreciated.
Related
I am trying to implement a custom appearing/disappearing animation for a modal UIViewController in my app.
I have published the code showing this error here.
Here is the content related:
/// The view controller from which I'm trying to display the modal
class ViewController: UIViewController {
#IBAction func tapped() {
/// The modal showing
ModalTestViewController.show()
}
//...
}
/// The displayed modal
open class ModalTestViewController: TransitioningModalViewController {
init() {
super.init(nibName: "ModalTestViewController", bundle: .main)
transitioningDelegate = self
// 1. when I put transitioningDelegate here, case 1
}
public required init?(coder: NSCoder) {
fatalError()
}
open override func viewDidLoad() {
super.viewDidLoad()
// 2. if I put transitioningDelegate here, case 2
}
#IBAction func tapped() {
// a tap on the overlayView of my modal
dismiss(animated: true)
}
static func show() {
let modal = ModalTestViewController()
modal.modalPresentationStyle = .overCurrentContext
DispatchQueue.main.async {
UIApplication.shared.delegate?.window??.rootViewController?.present(modal, animated: true)
}
}
}
/// The default modal view controller, which all modals in my app should inherit
open class TransitioningModalViewController: UIViewController {
// MARK: View Properties
#IBOutlet weak var overlayView: UIView!
}
extension TransitioningModalViewController: UIViewControllerTransitioningDelegate {
public func animationController(
forPresented presented: UIViewController,
presenting: UIViewController,
source: UIViewController
) -> UIViewControllerAnimatedTransitioning? {
return TransitioningModalViewControllerPresenter()
}
public func animationController(
forDismissed dismissed: UIViewController
) -> UIViewControllerAnimatedTransitioning? {
return TransitioningModalViewControllerDismisser()
}
}
private final class TransitioningModalViewControllerPresenter: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(
using transitionContext: UIViewControllerContextTransitioning?
) -> TimeInterval {
return 0.5
}
func animateTransition(
using transitionContext: UIViewControllerContextTransitioning
) {
let toViewController: TransitioningModalViewController = transitionContext.viewController(
forKey: UITransitionContextViewControllerKey.to
) as! TransitioningModalViewController
let duration = transitionDuration(using: transitionContext)
toViewController.overlayView.alpha = 0.0
UIView.animate(
withDuration: duration
) {
toViewController.overlayView.alpha = 0.65
} completion: { result in
transitionContext.completeTransition(result)
}
}
}
private final class TransitioningModalViewControllerDismisser: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(
using transitionContext: UIViewControllerContextTransitioning?
) -> TimeInterval {
return 0.5
}
func animateTransition(
using transitionContext: UIViewControllerContextTransitioning
) {
let fromViewController: TransitioningModalViewController = transitionContext.viewController(
forKey: UITransitionContextViewControllerKey.from
) as! TransitioningModalViewController
let duration = transitionDuration(using: transitionContext)
UIView.animate(
withDuration: duration
) {
fromViewController.overlayView.alpha = 0.0
} completion: { result in
transitionContext.completeTransition(result)
}
}
}
The idea behind this is that the modal appearance should not be the usual bottom-to-top animation, but instead the overlay view should go from hidden to an alpha of 0,65.
Case 1: when I put transitioningDelegate = self in init(), the animation is killed and nothing happens.
Case 2: when I put it into the viewDidLoad(), the appearing animation is the default bottom-to-top one, but the disappearing one is the expecting one (with the overlay view vanishing).
It looks like something is wrong with the initial transitioningDelegate setting but I can't find what.
Thank you for your help!
In your original code, you are setting the delegate here:
open override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
transitioningDelegate = self
}
However, .present(...) is called before viewDidAppear(...), so the controller is presented with default slide-up animation.
Setting the delegate in init() doesn't work, because we have override the default presentation process... and the presented controller's view is never added to the view hierarchy.
This "quick fix" should do the job...
First, in ModalTestViewController, move setting the delegate to init():
init() {
super.init(nibName: "ModalTestViewController", bundle: .main)
transitioningDelegate = self
}
then, in TransitioningModalViewControllerPresenter, add these lines before the animation:
func animateTransition(
using transitionContext: UIViewControllerContextTransitioning
) {
let toViewController: TransitioningModalViewController = transitionContext.viewController(
forKey: UITransitionContextViewControllerKey.to
) as! TransitioningModalViewController
// add these lines \/
// get the "from" view controller
let fromVC = transitionContext.viewController(forKey: .from)!
// get the "to" view controller's view
let toView = transitionContext.view(forKey: .to)!
// set the frame of the "to" view to the initialFrame (the current frame) of the "from" VC
toView.frame = transitionContext.initialFrame(for: fromVC)
// get the transition container view
let container = transitionContext.containerView
// add the "to" view to the view hierarchy
container.addSubview(toView)
// add these lines /\
let duration = transitionDuration(using: transitionContext)
toViewController.overlayView.alpha = 0.0
UIView.animate(
withDuration: duration
) {
toViewController.overlayView.alpha = 0.65
} completion: { result in
transitionContext.completeTransition(result)
}
}
Personally, to make this more flexible, I would get rid of your overlayView and set the alpha on the controller's view itself.
I am trying to create a share sheet to share a Text, it was working fine in iOS 14 but in iOS 15 it tells me that
'windows' was deprecated in iOS 15.0: Use UIWindowScene.windows on a
relevant window scene instead.
how can I make it work on iOS 15 with SwiftUI
Button {
let TextoCompartido = "Hola 😀 "
let AV = UIActivityViewController(activityItems: [TextoCompartido], applicationActivities: nil)
UIApplication.shared.windows.first?.rootViewController?.present(AV, animated: true, completion: nil)
}
I think you would be best served using SwiftUI APIs directly. Generally, I would follow these steps.
Create SwiftUI View named ActivityView that adheres to UIViewControllerRepresentable. This will allow you to bring UIActivityViewController to SwiftUI.
Create an Identifiable struct to contain the text you'd like to display in the ActivityView. Making this type will allow you to use the SwiftUI sheet API and leverage SwiftUI state to tell the app when a new ActivityView to be shown.
Create an optional #State variable that will hold on to your Identifiable text construct. When this variable changes, the sheet API will perform the callback.
When the button is tapped, update the state of the variable set in step 3.
Use the sheet API to create an ActivityView which will be presented to your user.
The code below should help get you started.
import UIKit
import SwiftUI
// 1. Activity View
struct ActivityView: UIViewControllerRepresentable {
let text: String
func makeUIViewController(context: UIViewControllerRepresentableContext<ActivityView>) -> UIActivityViewController {
return UIActivityViewController(activityItems: [text], applicationActivities: nil)
}
func updateUIViewController(_ uiViewController: UIActivityViewController, context: UIViewControllerRepresentableContext<ActivityView>) {}
}
// 2. Share Text
struct ShareText: Identifiable {
let id = UUID()
let text: String
}
struct ContentView: View {
// 3. Share Text State
#State var shareText: ShareText?
var body: some View {
VStack {
Button("Show Activity View") {
// 4. New Identifiable Share Text
shareText = ShareText(text: "Hola 😀")
}
.padding()
}
// 5. Sheet to display Share Text
.sheet(item: $shareText) { shareText in
ActivityView(text: shareText.text)
}
}
}
For the future, iOS 16 will have the ShareLink view which works like this:
Gallery(...)
.toolbar {
ShareLink(item: image, preview: SharePreview("Birthday Effects"))
}
Source: https://developer.apple.com/videos/play/wwdc2022/10052/
Time code offset: 25 minutes 28 seconds
To avoid warning, change the way you retrieve the window scene.
Do the following:
Button {
let TextoCompartido = "Hola 😀 "
let AV = UIActivityViewController(activityItems: [TextoCompartido], applicationActivities: nil)
let scenes = UIApplication.shared.connectedScenes
let windowScene = scenes.first as? UIWindowScene
windowScene?.keyWindow?.rootViewController?.present(AV, animated: true, completion: nil)
}
Tested in in iOS 15 with SwiftUI
func shareViaActionSheet() {
if vedioData.vedioURL != nil {
let activityVC = UIActivityViewController(activityItems: [vedioData.vedioURL as Any], applicationActivities: nil)
UIApplication.shared.currentUIWindow()?.rootViewController?.present(activityVC, animated: true, completion: nil)
}
}
To avoid iOS 15 method deprecation warning use this extension
public extension UIApplication {
func currentUIWindow() -> UIWindow? {
let connectedScenes = UIApplication.shared.connectedScenes
.filter { $0.activationState == .foregroundActive }
.compactMap { $0 as? UIWindowScene }
let window = connectedScenes.first?
.windows
.first { $0.isKeyWindow }
return window
}
}
you could try the following using the answer from: How to get rid of message " 'windows' was deprecated in iOS 15.0: Use UIWindowScene.windows on a relevant window scene instead" with AdMob banner?
Note that your code works for me, but the compiler give the deprecation warning.
public extension UIApplication {
func currentUIWindow() -> UIWindow? {
let connectedScenes = UIApplication.shared.connectedScenes
.filter({
$0.activationState == .foregroundActive})
.compactMap({$0 as? UIWindowScene})
let window = connectedScenes.first?
.windows
.first { $0.isKeyWindow }
return window
}
}
struct ContentView: View {
let TextoCompartido = "Hola 😀 "
var body: some View {
Button(action: {
let AV = UIActivityViewController(activityItems: [TextoCompartido], applicationActivities: nil)
UIApplication.shared.currentUIWindow()?.rootViewController?.present(AV, animated: true, completion: nil)
// This works for me, but the compiler give the deprecation warning
// UIApplication.shared.windows.first?.rootViewController?.present(AV, animated: true, completion: nil)
}) {
Text("Hola click me")
}
}
}
First of all I would like to thank anyone in advance for any help I get.
I have searched far and wide across the net and cannot find a solution to my issue. My issue is with an iOS game I am building using the SpriteKit framework. I have added a background song using an SKAudioNode and it works fine initially, but when I pause and play the game within a few seconds, the music does not begin playing again. I have tried lots of things like removing the SKAudioNode when the game is paused and adding it again when the game is resumed, but nothing has worked. I have posted a snippet of my code below keeping it as relevant as possible:
class GameScene: SKScene, SKPhysicsContactDelegate {
var backgroundMusic = SKAudioNode(fileNamed: "bg.mp3")
let pauseImage = SKTexture(imageNamed: "pause.png")
var pauseButton:SKSpriteNode!
let playImage = SKTexture(imageNamed: "play2.png")
var playButton:SKSpriteNode!
override func didMoveToView(view: SKView) {
self.addChild(backgroundMusic)
// create pause button
pauseButton = SKSpriteNode(texture: pauseImage)
pauseButton.position = CGPoint(x: self.size.width - pauseButton.size.width, y: pauseButton.size.height)
pauseButton.zPosition = 1
pauseButton.name = "pauseButton"
self.addChild(pauseButton)
// create play button
playButton = SKSpriteNode(texture: playImage)
playButton.position = CGPoint(x: self.size.width - playButton.size.width, y: -playButton.size.height)
playButton.zPosition = 1
playButton.name = "playButton"
self.addChild(playButton)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
if pauseButton.containsPoint(touch.locationInNode(self)) {
let blockOne = SKAction.runBlock({
self.pauseButton.position.y = - self.pauseButton.size.height
self.playButton.position.y = self.playButton.size.height
})
let blockTwo = SKAction.runBlock({
self.view?.paused = true
})
self.runAction(SKAction.sequence([blockOne, blockTwo]))
}
else if(playButton.containsPoint(touch.locationInNode(self))) {
self.playButton.position.y = -self.playButton.size.height
self.pauseButton.position.y = self.pauseButton.size.height
self.view?.paused = false
}
}
}
}
You'll want to look into using AVAudioPlayer and NSNotificationCenter to pass data around the game.
Start the background audio player in your actual GameViewController class.
It's better to do it this way then use SKAudioNode... That's more for sounds that are like sound effects relating to something that happened in gameplay.
By using AVAudioPlayer, the advantage is when the music is paused it's still cued up to play in it's previous spot.
This is one of the few things that will be running regardless of what's going on. So we put it in the GameViewController.
So here's an example of GameViewController code we'd need to start
import AVFoundation
var bgMusicPlayer:AVAudioPlayer?
Then in the GameViewController we make these functions as such
override func viewDidLoad() {
super.viewDidLoad()
// PlayBackgroundSound , PauseBackgroundSound will be your code to send from other "scenes" to use the audio player //
// NSNotificationCenter to pass data throughout the game //
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(GameViewController.playBackgroundSound(_:)), name: "PlayBackgroundSound", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(GameViewController.pauseBackgroundSound), name: "PauseBackgroundSound", object: nil)
}
func playBackgroundSound(notification: NSNotification) {
let name = notification.userInfo!["fileToPlay"] as! String
if (bgSoundPlayer != nil){
bgSoundPlayer!.stop()
bgSoundPlayer = nil
}
if (name != ""){
let fileURL:NSURL = NSBundle.mainBundle().URLForResource(name, withExtension: "mp3")!
do {
bgSoundPlayer = try AVAudioPlayer(contentsOfURL: fileURL)
} catch _{
bgSoundPlayer = nil
}
bgSoundPlayer!.volume = 1
bgSoundPlayer!.numberOfLoops = -1
// -1 will loop it forever //
bgSoundPlayer!.prepareToPlay()
bgSoundPlayer!.play()
}
}
func pauseBackgroundSound() {
if (bgSoundPlayer != nil){
bgSoundPlayer!.pause()
}
}
Then when you want to use the audio player in your pause or resume button functions.
Remember you need to have the player used in each scene.
import AVFoundation.AVAudioSession
override func didMoveToView(view: SKView) {
try! AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryAmbient)
}
Then if you want to pause or play something just use NSNotificationCenter.
// To Pause in a scene use this line of code in the function you need to pause the music
NSNotificationCenter.defaultCenter().postNotificationName("PauseBackgroundSound", object: self)
/// To Play initially or a new song .. use this line of code in the function you need to play the music
NSNotificationCenter.defaultCenter().postNotificationName("PlayBackgroundSound", object: self, userInfo: "FILE NAME OF BACKGROUND MUSIC")
I'm currently struggling with getting my TableView to update after I finish performing some functions called in viewDidLoad and viewDidAppear. I tried using self.tableView.reloadData() at the end of my viewDidLoad but it didn't work and upon reloading the tab, the app would crash.
Here is some of my code (I'm trying to fetch events from a Google Calendar and display it in a TableView). I'm trying to display an array of strings named listOfEvents and it is being populated after the tableView is already loaded.
I also tried adding self.tableView.reloadData() at the end of my fetchEvents() but it also killed my app upon reloading the tab
class CalendarViewController: UITableViewController {
var listOfEvents: [String] = []
private let kKeychainItemName = "Google Calendar API"
private let kClientID = "clientID"
// If modifying these scopes, delete your previously saved credentials by
// resetting the iOS simulator or uninstall the app.
private let scopes = [kGTLAuthScopeCalendarReadonly]
private let service = GTLServiceCalendar()
let output = UITextView()
// When the view loads, create necessary subviews
// and initialize the Google Calendar API service
override func viewDidLoad() {
super.viewDidLoad()
if let auth = GTMOAuth2ViewControllerTouch.authForGoogleFromKeychainForName(
kKeychainItemName,
clientID: kClientID,
clientSecret: nil) {
service.authorizer = auth
}
}
// When the view appears, ensure that the Google Calendar API service is authorized
// and perform API calls
override func viewDidAppear(animated: Bool) {
if let authorizer = service.authorizer,
canAuth = authorizer.canAuthorize where canAuth {
fetchEvents()
} else {
presentViewController(
createAuthController(),
animated: true,
completion: nil
)
}
}
// Construct a query and get a list of upcoming events from the user calendar
func fetchEvents() {
let query = GTLQueryCalendar.queryForEventsListWithCalendarId("primary")
query.maxResults = 10
query.timeMin = GTLDateTime(date: NSDate(), timeZone: NSTimeZone.localTimeZone())
query.singleEvents = true
query.orderBy = kGTLCalendarOrderByStartTime
service.executeQuery(
query,
delegate: self,
didFinishSelector: "displayResultWithTicket:finishedWithObject:error:"
)
}
// Display the start dates and event summaries in the UITextView
func displayResultWithTicket(
ticket: GTLServiceTicket,
finishedWithObject response : GTLCalendarEvents,
error : NSError?) {
if let error = error {
showAlert("Error", message: error.localizedDescription)
return
}
var eventString = ""
if let events = response.items() where !events.isEmpty {
for event in events as! [GTLCalendarEvent] {
let start : GTLDateTime! = event.start.dateTime ?? event.start.date
let startString = NSDateFormatter.localizedStringFromDate(
start.date,
dateStyle: .ShortStyle,
timeStyle: .ShortStyle
)
eventString += "\(startString) - \(event.summary)\n"
// An array holding all my upcoming events
listOfEvents.append("\(startString) - \(event.summary)")
print(listOfEvents)
}
} else {
eventString = "No upcoming events found."
}
output.text = eventString
self.tableView.reloadData()
}
// Creates the auth controller for authorizing access to Google Calendar API
private func createAuthController() -> GTMOAuth2ViewControllerTouch {
let scopeString = scopes.joinWithSeparator(" ")
return GTMOAuth2ViewControllerTouch(
scope: scopeString,
clientID: kClientID,
clientSecret: nil,
keychainItemName: kKeychainItemName,
delegate: self,
finishedSelector: "viewController:finishedWithAuth:error:"
)
}
// Handle completion of the authorization process, and update the Google Calendar API
// with the new credentials.
func viewController(vc : UIViewController,
finishedWithAuth authResult : GTMOAuth2Authentication, error : NSError?) {
if let error = error {
service.authorizer = nil
showAlert("Authentication Error", message: error.localizedDescription)
return
}
service.authorizer = authResult
dismissViewControllerAnimated(true, completion: nil)
}
// Helper for showing an alert
func showAlert(title : String, message: String) {
let alert = UIAlertController(
title: title,
message: message,
preferredStyle: UIAlertControllerStyle.Alert
)
let ok = UIAlertAction(
title: "OK",
style: UIAlertActionStyle.Default,
handler: nil
)
alert.addAction(ok)
presentViewController(alert, animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(self.listOfEvents.count)
return self.listOfEvents.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("Events Cell", forIndexPath: indexPath) as UITableViewCell
var event = ""
event = listOfEvents[indexPath.row]
cell.textLabel?.text = event
return cell
}
}
I would appreciate any help and insight :-) Thanks so much!
After output.text = eventString, you should reload the tableview.
I would like to open the camera and library in landscape mode, but I not be able to do it.
Please someone could explain and give me an example.
I have used the below code but seems some code is missing (for instance: import library), I will need a complete example, how I could implement, functions to open library and the camera and save in the camera roll.
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
if let connection = self.previewLayer?.connection {
var currentDevice: UIDevice = UIDevice.currentDevice()
var orientation: UIDeviceOrientation = currentDevice.orientation
var previewLayerConnection : AVCaptureConnection = connection
if (previewLayerConnection.supportsVideoOrientation)
{
switch (orientation)
{
case .Portrait:
previewLayerConnection.videoOrientation = AVCaptureVideoOrientation.Portrait
break
case .LandscapeRight:
previewLayerConnection.videoOrientation = AVCaptureVideoOrientation.LandscapeLeft
break
case .LandscapeLeft:
previewLayerConnection.videoOrientation = AVCaptureVideoOrientation.LandscapeRight
break
case .PortraitUpsideDown:
previewLayerConnection.videoOrientation = AVCaptureVideoOrientation.PortraitUpsideDown
break
default:
previewLayerConnection.videoOrientation = AVCaptureVideoOrientation.Portrait
break
}
}
}
}
Also indicate that I already have some code implemented then
I find it easier to implement without the use of additional libraries like AVFoundation.
I am currently using this code in order to open library and camera.
#IBAction func openLibrary(sender: AnyObject) { ///accion del boton Library
imagePicker.allowsEditing = false
imagePicker.sourceType = .PhotoLibrary
presentViewController(imagePicker, animated:true, completion: nil)
}
#IBAction func openCamera(sender: AnyObject) { ///accion del boton Camara
imagePicker.allowsEditing = false
imagePicker.sourceType = .Camera
presentViewController(imagePicker, animated:true, completion: nil)
}
in ur app delegate use this function:
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> Int {
if let presentedView = self.window?.rootViewController?.presentedViewController as? CameraViewController {
//cameraView
return Int(UIInterfaceOrientationMask.Landscape.rawValue)
}
} else {
//other view
}
}
Swift 2
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
if let presentedView = self.window?.rootViewController?.presentedViewController as? YourCameraViewController {
//cameraView
return UIInterfaceOrientationMask.Landscape
} else {
//other view
// return here ur orientation for other view
}
}