Attempt to present <a> on <a> while a presentation is in progress - swift

i know this has been asked before but i'm trying to figure out the issue with my project, as the title states (a) is trying to present on (a) i have checked all segue triggers to see if i accidentally set a segue to go to the same view controller that it is already on but this is not the case.
view controller 1 code
import UIKit
import UserNotifications
class NotificationViewController: UIViewController {
let isRegisteredForRemoteNotifications = UIApplication.shared.isRegisteredForRemoteNotifications
let current = UNUserNotificationCenter.current()
#IBAction func Notification(_ sender: Any) {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge], completionHandler: {didAllow, error in})
var i = 0
while i < 1{
current.getNotificationSettings(completionHandler: { (settings) in
if settings.authorizationStatus == .notDetermined {
// Notification permission has not been asked yet, go for it!
}
if settings.authorizationStatus == .denied {
i = i + 1
DispatchQueue.main.async {
self.performSegue(withIdentifier: "ToLocation", sender: self)
// Notification permission was previously denied, go to settings & privacy to re-enable
}
}
if settings.authorizationStatus == .authorized {
i = i + 1
DispatchQueue.main.async {
self.performSegue(withIdentifier: "ToLocation", sender: self)
// Notification permission was already granted
}
}
})
}
}
override func viewDidLoad() {
current.getNotificationSettings(completionHandler: { (settings) in
if settings.authorizationStatus == .notDetermined {
// Notification permission has not been asked yet, go for it!
}
if settings.authorizationStatus == .denied {
DispatchQueue.main.async {
self.performSegue(withIdentifier: "ToLocation", sender: self)
// Notification permission was previously denied, go to settings & privacy to re-enable
}
}
if settings.authorizationStatus == .authorized {
DispatchQueue.main.async {
self.performSegue(withIdentifier: "ToLocation", sender: self)
// Notification permission was already granted
}
}
})
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
view controller 2 code
import UIKit
import CoreLocation
class LocationViewController: UIViewController {
#IBOutlet weak var textview: UITextView!
let locationManager = CLLocationManager()
#IBAction func OnLocation(_ sender: Any) {
locationManager.delegate = self as? CLLocationManagerDelegate
var i = 0
while i < 1{
switch CLLocationManager.authorizationStatus() {
case .notDetermined:
// Request when-in-use authorization initially
locationManager.requestWhenInUseAuthorization()
break
case .restricted, .denied:
i = i + 1
DispatchQueue.main.async {
self.performSegue(withIdentifier: "ToLogin", sender: self)
}
// Disable location features
//disableMyLocationBasedFeatures()
break
case .authorizedWhenInUse:
i = i + 1
DispatchQueue.main.async {
self.performSegue(withIdentifier: "ToLogin", sender: self)
}
// Enable basic location features
//enableMyWhenInUseFeatures()
break
case .authorizedAlways:
// Enable any of your app's location features
// enableMyAlwaysFeatures()
break
}
}
}
override func viewDidLoad() {
func locationManager(_ manager: CLLocationManager,
didChangeAuthorization status: CLAuthorizationStatus) { switch status {
case .restricted, .denied:
self.performSegue(withIdentifier: "ToLogin", sender: self)
break
case .authorizedWhenInUse:
self.performSegue(withIdentifier: "ToLogin", sender: self)
break
case .authorizedAlways:
self.performSegue(withIdentifier: "ToLogin", sender: self)
break
case .notDetermined:
break
}
}
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
error message
018-02-27 23:06:41.534749+1030 Request[27358:1351259] Warning: Attempt
to present on
while a presentation
is in progress!

No no no no no. This is not the way. sorry about this plenty of no´s but completionHandler result parameter of the requestAuthorization method from UNUserNotification may be executed on a background thread.
tips for this function
#IBAction func Notification(_ sender: Any)
*should start with lowercase
*provide a name that the action it accomplish can be inferred by that name
*change the type of the sender (if you know it), that way you can explicitly call properties or methods on that object without cast.
continuing with the rest of the code inside the scope of the function. requestAuthorization have the only responsibility of that - ask for a permission - and the response is didAllow or error. You never check this and continue launching another block that response is also another thread
bottom line: your calling requestAuthorization and then getNotificationSettings inside a loop (why?), you have a lot of luck if this code execute 30% of the time.
So you should separate some of the code involving permissions, read some lines about GRASP principle, also read every chapter here

Your error message is pretty clear while a presentation is in progress. That mean that you try to present LocationViewController from another LocationViewController, but it still not presented yet.
Move your presentation logic to viewWillAppear method. It will help
UPDATE
1) remove your existing segue from Notification button to LocationViewController in storyboard
2) Add segue from NotificationViewController to LocationViewController like it shown on picture
3) Set name for this segue
Now try to run your project

Related

Remove done button in M13PDFKit swift

I'm using M13PDFKit and having a little problem. In the example the done button is not there, but when I try it shows. I don't know how to remove it. The done button looks like this. Im using Swift
in View controller just like this
import UIKit
import M13PDFKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "NEXT" {
let viewer: PDFKBasicPDFViewer = segue.destinationViewController as! PDFKBasicPDFViewer
viewer.enableBookmarks = true
viewer.enableBookmarks = true
viewer.enableOpening = true
viewer.enablePrinting = true
viewer.enableSharing = true
viewer.enableThumbnailSlider = true
//Load the document (pdfUrl represents the path on the phone of the pdf document you wish to load)
let pdfUrl = "/Users/developer/Library/Developer/CoreSimulator/Devices/C8DC5F44-B2FC-4FC3-8E10-B87FC00791FA/data/Containers/Data/Application/047BE9AD-F43C-421E-B5B5-943B0C79B592/Documents/Yudi Seven.pdf"
let document: PDFKDocument = PDFKDocument(contentsOfFile: pdfUrl, password: nil)
viewer.loadDocument(document)
}
}
}
Goto PDFKBasicPDFViewer.m disable the line(line 247) that highlighted in the first image and run the project....
Output:

performSegueWithIdentifier not working if called from viewDidLoad

I have a simple app with a loading screen. Here I check for some user details in NSUserDefaults and jump to either the login or the sign up screen.
The viewDidLoad() for the loading screen looks like this:
override func viewDidLoad()
{
super.viewDidLoad()
loadingVM = LoadingVM() as LoadingVM
print("LoadingVC")
checkStoredUser()
}
Here is the checkStoredUser()
func checkStoredUser()
{
storedUserStatus = loadingVM.returnStoredUserStatus()
if(storedUserStatus == true)
{
performSegueWithIdentifier("loadingToLoginVC", sender: self)
}
else
{
performSegueWithIdentifier("loadingToSignUpVC", sender: self)
}
}
As you can see, I decide where to go from here based on what the loadingVM.returnStoredUserStatus() returns. I am sure this returns what it's supposed to return but nothing happens.
Here is the prepareForSegue()
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
print("prepareForSegue")
if(segue.identifier == "loadingToSignUpVC")
{
let signUpViewCotroller = (segue.destinationViewController as! LocalSignUpVC)
}
else if(segue.identifier == "loadingToLoginVC")
{
print("loadingToLoginVC")
let loginViewCotroller = (segue.destinationViewController as! LoginVC)
}
}
I did some digging and found a weird suggestion that seems to be working but It's not very practical not to mention right to do it like this:
func checkStoredUser()
{
storedUserStatus = loadingVM.returnStoredUserStatus()
let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(1.0 * Double(NSEC_PER_SEC)))
dispatch_after(delayTime, dispatch_get_main_queue())
{
if(self.storedUserStatus == false)
{
self.performSegueWithIdentifier("loadingToSignUpVC", sender: self)
}
else
{
self.performSegueWithIdentifier("loadingToLoginVC", sender: self)
}
}
}
Can anyone explain to me what's going on here, why doesn't this work and how to make it work properly? It's the first time I encounter this and I can't seem to be able to find any info on this.
EXPLANATION:
Your View hasn't appeared yet when you call your checkStoredUser().
EASY FIX:
Put it in viewDidAppear() like this:
override func viewDidAppear(animated:Bool) {
super.viewDidAppear(false)
checkStoredUser()
}

Login Screen Always Shown

I have a login screen that prompts the user to login. After successful login the first time, I need the app to eliminate the login screen when the user opens the app again. I am using NSUserDefaults to set the successful login as true. However, the login screen is always shown to the user. Please help. Thanks.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
if (NSUserDefaults.standardUserDefaults().objectForKey("onoroff") != nil) {
self.performSegueWithIdentifier("segue", sender: nil)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func button(sender: AnyObject) {
if ((loginid.text == "Hussain") && (passwordText.text == "1234" ))
{
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "onoroff")
self.performSegueWithIdentifier("segue", sender: nil) }
else
{
failedtext.hidden = false }
}
Try adding
if (NSUserDefaults.standardUserDefaults().boolForKey("onoroff"))) {
self.performSegueWithIdentifier("segue", sender: nil)
}
in viewDidAppear method instead of viewDidLoad
Change objectForKey to boolForKey
if (NSUserDefaults.standardUserDefaults().boolForKey("onoroff")) {
self.performSegueWithIdentifier("segue", sender: nil)
}
Also do this
#IBAction func button(sender: AnyObject) {
if ((loginid.text == "Hussain") && (passwordText.text == "1234" ))
{
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "onoroff")
NSUserDefaults.standardUserDefaults().synchronize()
self.performSegueWithIdentifier("segue", sender: nil)
}
else
{
failedtext.hidden = false
}
}

Calling achievement screen in Swift

I am having some extreme difficulties calling the achievements screen in game center. I have already set up the achievements in iTunes connect and it pops up fine if I access the achievements screen through the leaderboard first. However; I would like to be able to press a specific achievement button and be directed directly to the achievements screen. Can any one help? I have searched high and low on the internet ( and read through all of the documentation). I have found many resources for implementing leaderboards, but not many resources for implementing achievements in swift. My code is below. Any suggestions for my last two functions?
override func viewDidLoad() {
super.viewDidLoad()
login()
}
func login() {
println("Game Center Login Called")
let localPlayer = GKLocalPlayer.localPlayer()
// Handle the authentication
localPlayer.authenticateHandler = {(Home: UIViewController!, error: NSError!) -> Void in
if Home != nil {
println("Authentication is being processed.")
self.presentViewController(Home, animated: true, completion: nil)
} else {
println("Player has been successfully authenticated.")
}
}
}
func showLeaderboard() {
let gkScore = GKScore(leaderboardIdentifier: "high_Score_Leader_Board")
gkScore.value = Int64(highscore)
GKScore.reportScores([gkScore], withCompletionHandler: ( { (error: NSError!) -> Void in
if (error != nil) {
// handle error
println("Error: " + error.localizedDescription);
} else {
println("Score reported: \(gkScore.value)")
}
}))
var gcViewController: GKGameCenterViewController = GKGameCenterViewController()
gcViewController.gameCenterDelegate = self
gcViewController.viewState = GKGameCenterViewControllerState.Leaderboards
gcViewController.leaderboardIdentifier = "high_Score_Leader_Board"
self.showViewController(gcViewController, sender: self)
self.presentViewController(gcViewController, animated: true, completion: nil)
}
#IBAction func gameCenterButtoPressed(sender: AnyObject) {
showLeaderboard()
}
func gameCenterViewControllerDidFinish(gcViewController: GKGameCenterViewController!)
{
self.dismissViewControllerAnimated(true, completion: nil)
}
func showAchievements() {
// show Achievements screen
}
#IBAction func achievementButtonPressed(sender: AnyObject) {
// Call show achievements function when button pressed
}
Instead of:
gcViewController.viewState = GKGameCenterViewControllerState.Leaderboards
I think what you'll want is:
gcViewController.viewState = GKGameCenterViewControllerState.Achievements
And I found this information in this related tutorial.

Locations in swift

I want to update the location when the user enters the app again. If a user opens the app you get correct data, but when you close the app (homebutton) and open it again it goes in the refresh function, but does not go in the location function. I can't get it. Here is my code:
import UIKit
import CoreLocation
var request : NSMutableURLRequest = NSMutableURLRequest()
class ViewController: UIViewController, CLLocationManagerDelegate {
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
}
func refresh(){
println("update")
locationManager.startUpdatingLocation()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!)
{
CLGeocoder().reverseGeocodeLocation(manager.location, completionHandler: {(placemarks, error)->Void in
if (error != nil)
{
println("Error: " + error.localizedDescription)
return
}
if (placemarks.count > 0)
{
let pm = placemarks[0] as! CLPlacemark
self.displayLocationInfo(pm)
}
else
{
println("Error with the data.")
}
})
}
in my app delegate i have this:
func applicationWillEnterForeground(application: UIApplication) {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
ViewController().refresh();
}
So it goes in the refresh function, but than it does noting.. What can it be?
Thank you!
in each call of applicationWillEnterForeground(application: UIApplication) you create a new object of ViewController, call refresh() on it and let it go away. This is not the view controller object, that is presented to the user.
Instead you must get the presented ViewController object and call refresh() on it.
It could be something like
func applicationWillEnterForeground(application: UIApplication) {
let viewController= self.window.rootViewController as! ViewController
viewController.refresh()
}
But how it looks exactly, depends on details in your code we don't know.
or delete that line altogether and subrcribe for the WillEnterForeground notification in your ViewController's viewDidLoad()
override func viewDidLoad()
{
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "applicationWillEnterForeground:", name: UIApplicationWillEnterForegroundNotification, object: nil)
}
func applicationWillEnterForeground(notification: NSNotification) {
self.refresh()
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
The challenge you have is that when you press the home key, your views do not unload but are merely suspended. Therefore with this, the viewDidLoad method does not get called, when your application resumes...
What you are actually doing is dealing with the application coming into the foreground which you have to register to receive notifications about. Add the following line to your viewDidLoad method:
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("applicationWillEnterForeground"), name: UIApplicationWillEnterForegroundNotification, object: nil)
That will add an observer for the UIApplicationWillEnterForegroundNotification event and then call the method applicationWillEnterForeground. Now all you need to do is create the method applicationWillEnterForeground and call your refresh method from within it.