I can't see why my loginViewController continues to have the dismiss X and the Signup button. I don't want either. I thought by simply not including them in my logInController.fields array that they would not appear, but that does not seem to be the case. Any help?
let logInController = PFLogInViewController()
logInController.delegate = self
self.presentViewController(logInController, animated:true, completion: nil)
logInController.fields = [PFLogInFields.UsernameAndPassword, PFLogInFields.LogInButton, PFLogInFields.PasswordForgotten]
I am getting an error that might mean something.
2015-12-22 21:35:29.463 App Name[5737:99658] Warning: Attempt to present <PFLogInViewController: 0x7fd318c56020> on <app name.loginViewController: 0x7fd318d6c120> whose view is not in the window hierarchy!
I figured it out.
Make sure to import ParseUI
You must present your viewController in viewDidAppear not in viewDidLoad.
Here is the code that I am using, it builds and runs fine.
class LoginViewController: PFLogInViewController, PFLogInViewControllerDelegate {
override func viewDidAppear(animated: Bool) {
let logInController = PFLogInViewController()
logInController.delegate = self
logInController.fields = [PFLogInFields.UsernameAndPassword, PFLogInFields.LogInButton, PFLogInFields.PasswordForgotten, PFLogInFields.SignUpButton]
self.presentViewController(logInController, animated:true, completion: nil)
}
Related
it's again me - learning swift.
Question is simple, i have control views like this:
[Initial]->[NotLogged]->[SignUp]
Now after sign up i have double dismiss, it's really ugly!
I wan't to go straight from SignUp to Initial page.
I tried this code but sadly it closing app without any error.
self.dismiss(animated: true, completion: {
let controllers = self.navigationController?.viewControllers
for vc in controllers! {
if vc is InitialViewController {
_ = self.navigationController?.popToViewController(vc as! InitialViewController, animated: true)
}
}
} )
pushViewController works same as above, there is output:
2020-01-29 20:30:38.342180+0100 BillyBill[44355:19540995] Can't end
BackgroundTask: no background task exists with identifier 10 (0xa), or
it may have already been ended. Break in
UIApplicationEndBackgroundTaskError() to debug.
Simply just pop to root view controller
self.navigationController?.popToRootViewController(animated: true)
You can just use self.navigationController?.popToRootViewController() command in place of
for vc in controllers! {
if vc is InitialViewController {
_ = self.navigationController?.popToViewController(vc as! InitialViewController, animated: true)
}
}
I am trying to send an email from within my game app. In one of my SKScenes I have a sprite when you press it, it calls FeedbackVC().sendEmail(). This opens up the email viewController, but it does not dismiss properly. Here is my entire FeedbackVC class. I used the function getTopMostViewController because without it I was getting the error "Warning: Attempt to present on whose view is not in the window hierarchy!". My code will successfully open the MFMailComposeViewController with the prefilled fields and if I press the send button it actually will send to the email to my email, but it won't close and if I try to cancel the email it won't close either. Why won't my viewController close so it will continue back to my game after the email is sent or canceled?
import Foundation
import MessageUI
class FeedbackVC: UINavigationController, MFMailComposeViewControllerDelegate {
func getTopMostViewController() -> UIViewController? {
var topMostViewController = UIApplication.shared.keyWindow?.rootViewController
while let presentedViewController = topMostViewController?.presentedViewController {
topMostViewController = presentedViewController
}
return topMostViewController
}
func sendEmail() {
if MFMailComposeViewController.canSendMail() {
let mail = MFMailComposeViewController()
mail.mailComposeDelegate = self
mail.setToRecipients(["support#supportemail.com"])
mail.setSubject("In-App Feedback")
mail.setMessageBody("", isHTML: false)
self.getTopMostViewController()!.present(mail, animated: true, completion: nil)
} else {
print("Failed To Send Email!")
}
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true, completion: nil)
}
}
I have also tried setting the UINavigationControllerDelegate in the sendEmail() function.
mail.delegate = self as? UINavigationControllerDelegate
I have also tried things like popping the view controller and going back to the top most view controller in the mailComposeController.
popToRootViewContoller(animated: true)
getTopMostViewController()?.dismiss(animated: true, completion: nil)
I've tried following the guide on, https://developer.apple.com/documentation/messageui/mfmailcomposeviewcontroller, but it didn't work as I think my scenario is different since I am going from a SKScene to the MFMailCompose ViewController then back to a SKScene.
I'm one of the other developers working on this project. Posting in case someone has similar problems.
We were attempting to call our FeedbackVC in a way that looked like this:
if nodeTapped.name == "Feedback" {
let vc = FeedbackVC()
vc.emailButtonTapped(foo)
}
This would create the FeedbackVC class, call the emailButtonTapped method, and then deallocate the class from memory upon exiting the if statement. This means that clicking cancel or send would attempt to access the deallocated space, causing an EXC_BAD_ACCESS error. I fixed this by declaring vc as a class variable instead of declaring it inside the if statement.
I try to authenticate the user of my app if they are not. I first read the value saved in UserDefaults to know if they are logged in, and if not, I want to show the ViewController to allow them to log in.
But anyway, the LogInViewController won't show, this is my code :
import UIKit
import MapKit
class MainViewController: UIViewController, CLLocationManagerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
if(!UserDefaults.standard.bool(forKey: "userIsConnected")){
let LogVC = self.storyboard?.instantiateViewController(withIdentifier: "LogInViewController") as! LogInViewController
show(LogVC, sender: self)
}
}
I also tried without the 'if' condition, and it doesn't work anymore
EDIT :
I also tried presentVC() and I had the error "Attempt to present on whose view is not in the window hierarchy"
Thanks for help
You are trying to login in the MainViewController. You should avoid when possible managing your login in a view controller. The login is more effective if managed in your AppDelegate.
Add this code to didFinishLaunchingWithOptions in AppDelegate.swift
if !UserDefaults.standard.bool(forKey: "userIsConnected") {
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
let loginViewController = storyboard.instantiateViewController(withIdentifier: "LogInViewController")
window?.makeKeyAndVisible()
window?.rootViewController?.present(loginViewController, animated: true, completion: nil
}
Make sure you can access UserDefaults.
When I dismiss an instance of MFMailComposeViewController or MFMessageComposeViewController that is presented modally from the third viewController in a navigation stack, the navigation stack is reset, and the root VC is reloaded. How can I prevent this behavior and remain on the original presenting viewController (third VC in the stack)? I get the same behavior whether I call dismiss from the presenting VC, the presented VC, or the navigationController.
This has been asked before, but I have not seen a solution.
App Structure looks like this:
TabBarController
Tab 1 - TripsNavController
-> Trips IntroductionVC (root VC) segue to:
-> TripsTableViewController segue to:
-> TripEditorContainerVC
- TripEditorVC (child of ContainerVC)
- HelpVC (child of ContainerVC)
Tab 2...
Tab 3...
Tab 4...
In the TripEditorVC I present the MFMailComposeViewController. The functions below are declared in an extension to UIViewController that adopts the MFMailComposeViewControllerDelegate protocol
func shareWithEmail(message: NSAttributedString) {
guard MFMailComposeViewController.canSendMail() else {
showServiceError(message: "Email Services are not available")
return
}
let composeVC = MFMailComposeViewController()
composeVC.setSubject("My Trip Plan")
composeVC.setMessageBody(getHTMLforAttributedString(attrStr: message), isHTML: true)
composeVC.mailComposeDelegate = self
present(composeVC, animated: true, completion: nil)
}
Then in the delegate method I dismiss the MFMailComposeVC:
public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
switch result {
case .sent:
print("Mail sent")
case .saved:
print("Mail saved")
case .cancelled:
print("Mail cancelled")
case .failed:
print("Send mail failed")
}
if error != nil {
showServiceError(message: "Error: \(error!.localizedDescription)")
}
dismiss(animated: true, completion: nil)
}
I have tried the following to present and dismiss and get the same behavior, i.e.: the TripsNavController clears the nav stack and reloads the TripsIntroductionVC as its root VC:
self.present(composeVC, animated: true, completion: nil)
self.parent?.present(composeVC, animated: true, completion: nil)
self.parent?.navigationController?.present(composeVC, animated: true, completion: nil)
self.navigationController?.present(composeVC, animated: true, completion: nil)
You can also check with presentingViewController?.dismiss method to get the solution.
I have tried with following navigation stack.
I am able to send email successfully from Container VC's Send Email button using your code only.
Can you please check and verify navigation flow?
Please let me know if you still face any issue.
dismiss(animated: true, completion: nil)
to
self.dismiss(animated: true, completion: nil)
Try this
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let secondViewController = storyboard.instantiateViewControllerWithIdentifier("secondViewControllerId") as! SecondViewController
self.presentViewController(secondViewController, animated: true, completion: nil)
or
https://stackoverflow.com/a/37740006/8196100
Just Use Unwind Segue its Pretty simple and perfect in your case
I have used many times..
just look at this Unwind segue's example
. If u still don't able to find answer or not able to understand the Unwind segue then just reply to me.
Hope this will solve your problem.
I found the problem with my navigation stack today. I built a simple
project that duplicated the tabBarController/NavigationControler architecture of my problem project, component by component, until dismissing a MFMailComposeViewController caused my navigation stack to reset as described in my original post.
That immediately pointed to the source of the bug. In my subclassed UINavigationCotroller I was instantiating the root viewController in code so that I could skip an introductory view if the user had set a switch in the apps settings. In order to pick up changes in that switch setting I was calling my instantiation code in viewDidAppear of the navigationController. That worked fine EXCEPT when dismissing the mailComposeVC. The fix was to add a guard statement in viewDidAppear to return if the navControllers viewController collection was not empty, and send and respond to an NSNotification when the switch was changed.
class TopNavigationController: UINavigationController {
var sectionType: SectionType?
var defaults = UserDefaults.standard
var showIntroFlag: Bool = true
override func viewDidLoad() {
super.viewDidLoad()
// Handle initial load of the tab bar controller where we are not sent a sectionType
if sectionType == nil {
sectionType = .groups
}
setShowIntroFlag()
NotificationCenter.default.addObserver(self, selector: #selector(resetControllers), name: NSNotification.Name(rawValue: "kUserDidChangeShowIntros"), object: nil)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
guard self.viewControllers.isEmpty else {
return
}
loadControllers()
}
func setShowIntroFlag() {
showIntroFlag = true
// Check NSUserDefaults to see if we should hide the Intro Views for all sections
if defaults.bool(forKey: "SHOW_SECTION_INTROS") == false {
showIntroFlag = false
}
}
func loadControllers() {
if showIntroFlag == true {
showIntro()
} else {
skipIntro()
}
}
func resetControllers() {
setShowIntroFlag()
loadControllers()
}
I created a custom alert in a viewcontroller, following the guidelines of the most voted answer of that question:
https://stackoverflow.com/a/37275840/6196609
I use this to display the alert, it is used as a "loading".
let pending = UIAlertController()
override func viewDidLoad() {
super.viewDidLoad()
[…]
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let pending = storyboard.instantiateViewControllerWithIdentifier("alertaLoad")
pending.modalPresentationStyle = UIModalPresentationStyle.OverCurrentContext
pending.modalTransitionStyle = UIModalTransitionStyle.CrossDissolve
[…]
}
to show:
self.presentViewController(self.pending, animated: true, completion: nil)
I succeeded in showing it, but I need to terminate it by the viewcontroller that invoked it after the end of my process, not by itself as was done in the example I quoted.
I've tried this but nothing happens.
self.pending.dismissViewControllerAnimated(false, completion: { (vetor) -> Void in
[…]
})
How could I do this correctly?
Call dismisson the presenting UIViewController, not on the presented one:
self.dismiss(animated: true) {
// go on
}