I'm implementing rewarded ads in a chat application and using tab bars with it. I'm having a problem with rewarded that whenever I call the function rewardedAd = createAndLoadRewardedAd() in viewDidLoad and the ad gets presented when I dismiss the ad the function rewardedAdDidDismiss gets called without a problem.
override func viewDidLoad() {
super.viewDidLoad()
timerLabelDisplay.layer.opacity = 0.0
rewardedAd = createAndLoadRewardedAd()
}
Now as we all now viewDidLoad will be called only one time, and this will not help me if i change the tab bar and get back to it, this mean my ad won't load. So what i did, is i put the rewardedAd = createAndLoadRewardedAd() in viewWillAppear
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
removeAdsFreeButton.isEnabled = false
removeAdsFreeButton.backgroundColor = .gray
rewardedAd = createAndLoadRewardedAd()
}
Now when i did this everything seems to be perfect, when i change the tab bar and go back again, my reward ad is loading without a problem. However now, when the ad is dissmissed, the function rewardedAdDidDismiss() is not getting called, and i need it to be called and i didn't know why?
here it is the full code:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
removeAdsFreeButton.isEnabled = false
removeAdsFreeButton.backgroundColor = .gray
rewardedAd = createAndLoadRewardedAd()
}
override func viewDidLoad() {
super.viewDidLoad()
timerLabelDisplay.layer.opacity = 0.0
}
func createAndLoadRewardedAd() -> GADRewardedAd? {
rewardedAd = GADRewardedAd(adUnitID: "ca-app-pub-3940256099942544/1712485313")
rewardedAd?.load(GADRequest()) { error in
if error != nil {
print("Loading reward ad failed there is a problem here")
} else {
UIView.animate(withDuration: 1.0) {
self.removeAdsFreeButton.isEnabled = true
self.removeAdsFreeButton.layer.backgroundColor = UIColor(named: "ChatiwRed")!.cgColor
self.removeAdsFreeButton.layer.backgroundColor = UIColor(named: "ChatiwYello")!.cgColor
self.removeAdsFreeButton.layer.backgroundColor = UIColor(named: "ChatiwRed")!.cgColor
}
self.removeAdsFreeButton.pulseAnimation()
}
}
return rewardedAd
}
var didUserGotReward = false
func rewardedAdDidDismiss(_ rewardedAd: GADRewardedAd) {
self.rewardedAd = createAndLoadRewardedAd()
print("ok now show the timer")
if didUserGotReward {
UIView.animate(withDuration: 5) {
self.timerLabelDisplay.layer.opacity = 1.0
}
timerLabelDisplay.pulseAnimation()
didUserGotReward = false
}
}
Related
Good day everyone,
I have root view controller set up as my HomeViewController, in this project i am using token based authentication ( which i am storing in user defaults ) and i am using token for all my API calls.
I have a check in my viewWillAppear method to check if there is access token present and then i make the api call in viewDidAppear to populate the collection view, and this works perfectly fine at all times except the first time.
If I log in for the first time it hits the viewWillAppear, viewDidAppear and then login screen pops up and once i authenticate the user and save it in the UserDefaults, dismiss the login screen and in the HomeViewController all i get is a spinner ( which means that viewDidAppear is also been called ) but if i close the app and open it again it all works fine.
What can i change in my code to make it work in the first time please and thank you!!
class HomeViewController: UIViewController {
// MARK: - Properties
let refreshControl = UIRefreshControl()
var publishedReportList: [ReportListDetail] = []
private let reportsCollectionView: UICollectionView = {
let viewLayout = UICollectionViewFlowLayout()
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: viewLayout)
collectionView.register(ReportsCollectionViewCell.self, forCellWithReuseIdentifier: ReportsCollectionViewCell.identifier)
collectionView.backgroundColor = .systemBackground
return collectionView
}()
// MARK: - Initialisation
override func viewDidLoad() {
super.viewDidLoad()
reportsCollectionView.delegate = self
reportsCollectionView.dataSource = self
print(publishedReportList.count)
refreshControl.tintColor = .blue
refreshControl.addTarget(self, action: #selector(pullToRefresh), for: .valueChanged)
reportsCollectionView.addSubview(refreshControl)
reportsCollectionView.alwaysBounceVertical = true
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// check auth status
handleNotAuthenticated()
}
override func viewDidAppear(_ animated: Bool) {
// call for reports
getReportUserLayout()
}
// MARK: - Handlers
#objc func pullToRefresh() {
// Code to refresh table view
getReportUserLayout()
}
fileprivate func getReportUserLayout() {
// publishedReportList.removeAll()
whySuchEmptyLabel.isHidden = true
spinner.show(in: view)
spinner.textLabel.text = "Loading Reports.."
DispatchQueue.main.async {
ReportsManager.shared.getReportData { [weak self] (listOfReports) in
guard let strongSelf = self else { return }
strongSelf.publishedReportList = listOfReports
if listOfReports.count == 0 {
strongSelf.whySuchEmptyLabel.isHidden = false
}
strongSelf.reportsCollectionView.reloadData()
strongSelf.spinner.dismiss()
strongSelf.refreshControl.endRefreshing()
}
}
}
private func handleNotAuthenticated() {
if UserDefaults.standard.string(forKey: "accessToken") == nil {
// show login view controller
let loginVC = LoginViewController()
loginVC.modalPresentationStyle = .overCurrentContext
present(loginVC, animated: false)
}
}
}
You are in the HomeViewController and presenting loginVC, so it will not trigger viewDidAppear or viewWillAppear because it is not disappeared from the app. You have to use closure or delegate or notification to communicate back to the HomeViewController. You can also use the Combine framework and save the state. Here is an example of using delegate.
// Add protocol
protocol ViewControllerDelegate {
func loggedIn()
}
class HomeViewController: UIViewController, ViewControllerDelegate {
private func handleNotAuthenticated() {
if UserDefaults.standard.string(forKey: "accessToken") == nil {
let loginVC = LoginViewController()
loginVC.modalPresentationStyle = .overCurrentContext
loginVC.viewDelegate = self
present(loginVC, animated: false)
}
}
}
class LoginViewController: UIViewController {
var viewDelegate: ViewControllerDelegate? = nil
func userLoggedIn() {
self.viewDelegate?.loggedIn()
}
}
I have an Onboarding controller, when a user has completed Onboarding I am writing a value in UserDefaults and then dismissing the OnboardingViewController.
My base viewcontroller however is pushing them back to the Onboarding flow as UserDefaults.standard.bool(forKey: "ONBOARDING|COMPLETE") is returning false.
If I restart my app however is will return true and they are pushed to the correct VC.
My Base ViewController
class ViewController: UIViewController {
var onboardingComplete: Bool {
get {
return UserDefaults.standard.bool(forKey: "ONBOARDING|COMPLETE")
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print(UserDefaults.standard.bool(forKey: "ONBOARDING|COMPLETE"))
print(onboardingComplete)
setBaseViewController()
}
fileprivate func setBaseViewController() {
if onboardingComplete {
print("has completed onboarding")
} else {
let layout = UICollectionViewFlowLayout()
navigationController?.present(OnboardingController(collectionViewLayout: layout), animated: true, completion: { })
}
}
}
My Onboarding controller has a method in which I am using to set the value as complete
#objc func handleCompleteOnboarding() {
dismiss(animated: true) {
DispatchQueue.main.async {
UserDefaults.standard.set(true, forKey: "ONBOARDING|COMPLETE")
}
}
}
You are setting the value in the completion handler.
Move this outside of dismiss.
#objc func handleCompleteOnboarding() {
UserDefaults.standard.set(true, forKey: "ONBOARDING|COMPLETE")
dismiss(animated: true)
}
You have essentially created a race condition between you setting the value and your viewDidAppear method being called.
I don't know why but even after printing Admop interstitialWillPresentScreen,
ad is not showing.
func interstitialWillPresentScreen(_ ad: GADInterstitial) {
print("Admop interstitialWillPresentScreen")
}
appDelegate
GADMobileAds.configure(withApplicationID: "ca-app-pub-3940256099942544/4411468910")
ViewController
override func viewDidLoad() {
super.viewDidLoad()
interstitial = GADInterstitial(adUnitID: "ca-app-pub- 3940256099942544/4411468910")
interstitial.delegate=self
let request = GADRequest()
interstitial.load(request)
}
func didSelectRowAt(indexPath:IndexPath){
<-- I want to show ad when this function is called back from table view.
if interstitial.isReady {
print("Ad showing")
interstitial.present(fromRootViewController: self)
} else {
print("Ad wasn't ready")
}
but I think the problem is that...
I don't have view from main.storyboard sth...
I generate all view from code in viewDidLayoutSubviews method.
I guess...this is the reason...???
I don't find any error in code and because it comes to interstitialWillPresentScreen method...
I'm trying to setup iAd after clicking cancel button on JSSAlert. I have in JSSAlert function that set alpha for full view 0.7.. And in view controller I have function for iAd and set back alpha to 1.0...
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = "Vyhodnotenie testu"
self.showAlert()
}
func showAlert() {
func callback(){}
if numberOfPoints > 49 {
let customIcon = UIImage(named: "smile")
let alertview = JSSAlertView().show(self, title: "Gratulujeme! Uspeli ste.", text: "Dokončili ste test s počtom bodov \(numberOfPoints + 1) z \(maximumNumberOfPoints)!", buttonText: "OK!", color: UIColorFromHex(0x22c411, alpha: 1), iconImage: customIcon)
alertview.setTextTheme(.Light)
alertview.addAction(myCancelCallback)
self.navigationController?.navigationBar.alpha = 0.7
} else {
let customIcon = UIImage(named: "sad")
let alertview = JSSAlertView().show(self, title: "Ľutujeme! Neuspeli ste.", text: "Dokončili ste test s počtom bodov \(numberOfPoints + 1) z \(maximumNumberOfPoints)!", buttonText: "OK!", color: UIColorFromHex(0xd20606, alpha: 1), iconImage: customIcon)
alertview.addAction(myCancelCallback)
alertview.setTextTheme(.Light)
self.navigationController?.navigationBar.alpha = 0.7
}
}
func myCancelCallback() {
self.navigationController?.navigationBar.alpha = 1.0
self.interstitialPresentationPolicy = ADInterstitialPresentationPolicy.Automatic
}
func interstitialAdWillLoad(interstitialAd: ADInterstitialAd!) {
}
func interstitialAdDidLoad(interstitialAd: ADInterstitialAd!) {
interstitialAdView = UIView()
interstitialAdView.frame = self.view.bounds
view.addSubview(interstitialAdView)
interstitialAd.presentInView(interstitialAdView)
UIViewController.prepareInterstitialAds()
}
func interstitialAdActionDidFinish(var interstitialAd: ADInterstitialAd!) {
interstitialAd = nil
interstitialAdView.removeFromSuperview()
}
func interstitialAdActionShouldBegin(interstitialAd: ADInterstitialAd!, willLeaveApplication willLeave: Bool) -> Bool {
return true
}
func interstitialAd(interstitialAd: ADInterstitialAd!, didFailWithError error: NSError!) {
}
func interstitialAdDidUnload(var interstitialAd: ADInterstitialAd!) {
interstitialAd = nil
interstitialAdView.removeFromSuperview()
}
Alpha back to 1.0 in function myCancelCallback is working but iAd is delayed.. What can cause that delay? Or how can I deal with it?
I want to show iAd immediately after pressing OK!.
Video how it's working:
https://www.youtube.com/watch?v=r6LKN-cjaz8&feature=youtu.be
Update:
iAd App Network Shutdown As of December 31, 2016, the iAd App Network
is no longer available. If you'd like to promote your apps, you can
advertise using Search Ads, Apple News, or third party networks and
advertising sellers.
Reference: https://developer.apple.com/support/iad/
Here is what your gonna do, you have to create the interstitial including close button programmatically , i just made you a sample :
Add row in info.plist : View controller-based status bar appearance -> NO
in AppDelegate didFinishLaunchingWithOptions method add the following line to insure that status bar will be not hidden :
UIApplication.sharedApplication().setStatusBarHidden(false, withAnimation: UIStatusBarAnimation.None)
Here is a full sample view controller of how your gonna add the interstitial programmatically , but you only need to write animation when the interstitial is showing instead of just using addSubView. you can use animate transform translation you will find a lot of samples about that.
import UIKit
import iAd
class ViewController: UIViewController, ADInterstitialAdDelegate {
var interstitialAd:ADInterstitialAd!
var interstitialAdView: UIView = UIView()
var closeButton:UIButton!
override func viewDidLoad() {
super.viewDidLoad()
NSTimer.scheduledTimerWithTimeInterval(5, target: self, selector: "loadInterstitialAd", userInfo: nil, repeats: false)
}
func loadInterstitialAd() {
interstitialAd = ADInterstitialAd()
interstitialAd.delegate = self
}
func interstitialAdWillLoad(interstitialAd: ADInterstitialAd!) {
print("interstitialAdWillLoad")
}
func interstitialAdDidLoad(interstitialAd: ADInterstitialAd!) {
UIApplication.sharedApplication().setStatusBarHidden(false, withAnimation: UIStatusBarAnimation.Fade)
print("interstitialAdDidLoad")
UIApplication.sharedApplication().statusBarHidden = true
interstitialAdView = UIView()
interstitialAdView.frame = self.view.bounds
self.navigationController?.navigationBar.addSubview(interstitialAdView)
closeButton = UIButton(frame: CGRect(x: 15, y: 15, width: 20, height: 20))
//add a cross shaped graphics into your project to use as close button
closeButton.setBackgroundImage(UIImage(named: "close"), forState: UIControlState.Normal)
closeButton.addTarget(self, action: Selector("close"), forControlEvents: UIControlEvents.TouchDown)
self.navigationController?.navigationBar.addSubview(closeButton)
interstitialAd.presentInView(interstitialAdView)
UIViewController.prepareInterstitialAds()
}
func close() {
interstitialAdView.removeFromSuperview()
closeButton.removeFromSuperview()
interstitialAd = nil
UIApplication.sharedApplication().setStatusBarHidden(false, withAnimation: UIStatusBarAnimation.Fade)
}
func interstitialAdActionDidFinish(interstitialAd: ADInterstitialAd!) {
print("interstitialAdActionDidFinish")
UIApplication.sharedApplication().setStatusBarHidden(false, withAnimation: UIStatusBarAnimation.Fade)
}
func interstitialAdActionShouldBegin(interstitialAd: ADInterstitialAd!, willLeaveApplication willLeave: Bool) -> Bool {
return true
}
func interstitialAd(interstitialAd: ADInterstitialAd!, didFailWithError error: NSError!) {
print("didFailWithError")
}
func interstitialAdDidUnload(interstitialAd: ADInterstitialAd!) {
print("interstitialAdDidUnload")
close()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
}
Update: It might be you just have to call : UIViewController.prepareInterstitialAds() in viewDidLoad of your class to download the content of the interstitial so whenever you ask to present it will be ready and then might be no delay.
I am trying to figure out how to switch over from my banner view iAds to interstitial iAds in order to free up space for a tabbed controller. For some reason I am completely unable to find any resource for even getting started on these ads with swift.
Could anyone please give me some information on interstitial iAds with Swift and how I can implement them in a project.
Here is a relatively cleaner and easier to follow way to implement Interstitial Ads since this way doesn't require the use of NSNotificationCentre
import UIKit
import iAd
class ViewController: UIViewController, ADInterstitialAdDelegate {
var interstitialAd:ADInterstitialAd!
var interstitialAdView: UIView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
loadInterstitialAd()
}
func loadInterstitialAd() {
interstitialAd = ADInterstitialAd()
interstitialAd.delegate = self
}
func interstitialAdWillLoad(interstitialAd: ADInterstitialAd!) {
}
func interstitialAdDidLoad(interstitialAd: ADInterstitialAd!) {
interstitialAdView = UIView()
interstitialAdView.frame = self.view.bounds
view.addSubview(interstitialAdView)
interstitialAd.presentInView(interstitialAdView)
UIViewController.prepareInterstitialAds()
}
func interstitialAdActionDidFinish(interstitialAd: ADInterstitialAd!) {
interstitialAdView.removeFromSuperview()
}
func interstitialAdActionShouldBegin(interstitialAd: ADInterstitialAd!, willLeaveApplication willLeave: Bool) -> Bool {
return true
}
func interstitialAd(interstitialAd: ADInterstitialAd!, didFailWithError error: NSError!) {
}
func interstitialAdDidUnload(interstitialAd: ADInterstitialAd!) {
interstitialAdView.removeFromSuperview()
}
And it may help if you put println("Function Name") in each function just to keep track of your Interstitial Ads process. If you have any questions and or a way to improve this block of code please leave a comment. Thank You
Here is how i use in my project
//adding iAd framework
import iAd
//conform iAd delegate
class ViewController: UIViewController,ADInterstitialAdDelegate
//create instance variable
var interstitial:ADInterstitialAd!
//default iAd interstitials does not provide close button so we need to create one manually
var placeHolderView:UIView!
var closeButton:UIButton!
override func viewDidLoad() {
super.viewDidLoad()
//iAD interstitial
NSNotificationCenter.defaultCenter().addObserver(self, selector: ("runAd:"), name:UIApplicationWillEnterForegroundNotification, object: nil)
}
//iAD interstitial
func runAd(notification:NSNotification){
var timer = NSTimer.scheduledTimerWithTimeInterval(3.0, target: self, selector: Selector("dislayiAdInterstitial"), userInfo: nil, repeats: false)
cycleInterstitial()
}
func cycleInterstitial(){
// Clean up the old interstitial...
// interstitial.delegate = nil;
// and create a new interstitial. We set the delegate so that we can be notified of when
interstitial = ADInterstitialAd()
interstitial.delegate = self;
}
func presentInterlude(){
// If the interstitial managed to load, then we'll present it now.
if (interstitial.loaded) {
placeHolderView = UIView(frame: self.view.frame)
self.view.addSubview(placeHolderView)
closeButton = UIButton(frame: CGRect(x: 270, y: 25, width: 25, height: 25))
closeButton.setBackgroundImage(UIImage(named: "error"), forState: UIControlState.Normal)
closeButton.addTarget(self, action: Selector("close"), forControlEvents: UIControlEvents.TouchDown)
self.view.addSubview(closeButton)
interstitial.presentInView(placeHolderView)
}
}
// iAd Delegate Mehtods
// When this method is invoked, the application should remove the view from the screen and tear it down.
// The content will be unloaded shortly after this method is called and no new content will be loaded in that view.
// This may occur either when the user dismisses the interstitial view via the dismiss button or
// if the content in the view has expired.
func interstitialAdDidUnload(interstitialAd: ADInterstitialAd!){
placeHolderView.removeFromSuperview()
closeButton.removeFromSuperview()
interstitial = nil
cycleInterstitial()
}
func interstitialAdActionDidFinish(_interstitialAd: ADInterstitialAd!){
placeHolderView.removeFromSuperview()
closeButton.removeFromSuperview()
interstitial = nil
println("called just before dismissing - action finished")
}
// This method will be invoked when an error has occurred attempting to get advertisement content.
// The ADError enum lists the possible error codes.
func interstitialAd(interstitialAd: ADInterstitialAd!,
didFailWithError error: NSError!){
cycleInterstitial()
}
//Load iAd interstitial
func dislayiAdInterstitial() {
//iAd interstitial
presentInterlude()
}
func close() {
placeHolderView.removeFromSuperview()
closeButton.removeFromSuperview()
interstitial = nil
}
I know this is old - but I just came across
override func prepareForSegue(segue: UIStoryboardSegue,
sender: AnyObject?) {
let destination = segue.destinationViewController
as UIViewController
destination.interstitialPresentationPolicy =
ADInterstitialPresentationPolicy.Automatic
}
If you add this to your app delegate, you'll avoid that 3 second delay suggested above.
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
UIViewController.prepareInterstitialAds()
return true
}