I am using my custom alert to log out of the application
in custom alert, I have tried to log out with firebase after the successful logout using navigation push controller to sign in the controller but it will not redirect to sign in screen
Here is my custom alert code
let customAlert = self.storyboard?.instantiateViewController(withIdentifier: "CustomAlertID") as! AlertViewController
customAlert.titleLbl = "Log out"
customAlert.imageTitle = "logout"
customAlert.descryptionText = "You will be returned to the login screen. Are you sure you want to logout?"
customAlert.okBtnText = "LOG OUT"
DispatchQueue.main.async {
customAlert.providesPresentationContextTransitionStyle = true
customAlert.definesPresentationContext = true
customAlert.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
customAlert.modalTransitionStyle = UIModalTransitionStyle.crossDissolve
self.present(customAlert, animated: true, completion: nil)
}
Here is my logout click button code
let firebaseAuth = Auth.auth()
do {
try firebaseAuth.signOut()
let controller = self.storyboard?.instantiateViewController(withIdentifier: "SignInViewController") as! SignInViewController
self.navigationController?.pushViewController(controller, animated: true)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.navigationController?.popToRootViewController(animated: true)
}
let domain = Bundle.main.bundleIdentifier!
UserDefaults.standard.removePersistentDomain(forName: domain)
UserDefaults.standard.synchronize()
print(Array(UserDefaults.standard.dictionaryRepresentation().keys).count)
} catch let signOutError as NSError {
self.dismiss(animated: true, completion: nil)
print("Error signing out: %#", signOutError)
}
}
without the alert view, it will works
In order for the navigationController property to not be equal to nil, you must add your custom alertViewController to the navigation stack using pushViewController method. In your case, you are only presenting a custom alertViewController that does not know anything about your navigationController
There is another way to implement:
After initializing a custom view controller, you should pass it the necessary closure functions that should work after pressing the button
Example:
let customAlert = self.storyboard?.instantiateViewController(withIdentifier: "CustomAlertID") as! AlertViewController
customAlert.titleLbl = "Log out"
customAlert.imageTitle = "logout"
customAlert.onClickExit = { [weak self] in
self?.navigationController?.popViewController(animated: true)
}
...
After Logout, you should set Another Root view controller and Your navigationController Stack Must be Clear.
let controller = self.storyboard?.instantiateViewController(withIdentifier: "SignInViewController") as! SignInViewController
let signInNavController = UINavigaionController(rootViewController: controller)
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController = signInNavController
Related
I am using deeplink in my application and from the central area, I am trying push a screen in another ViewController based on my deeplink.
The issues I have is that when I cancel the pushed ViewController, the entire application stack is dismissed and I just want to pop back to the presenting viewcontroler.
func getCurrentNanvigationController() -> UINavigationController? {
//targeted UINavigationController
var navigationController: UINavigationController? = nil
if let nav = window?.rootViewController as? UINavigationController {
if let topNav = window?.topViewController()?.navigationController {
navigationController = topNav
}
else{
navigationController = nav
}
}
// Wallet Module is the stand alone module, which means its not embeded in the Navigator app
else if let tabBar = window?.rootViewController as? UITabBarController,
let nav = tabBar.selectedViewController as? UINavigationController {
navigationController = nav
}
else {
//should not happen, window root controller shouldbe be either UINavigationController or UITabBarController
}
return navigationController
}
public extension UIWindow {
func topViewController() -> UIViewController? {
var top = self.rootViewController
while true {
if let presented = top?.presentedViewController {
top = presented
} else if let nav = top as? UINavigationController {
top = nav.visibleViewController
} else if let tab = top as? UITabBarController {
top = tab.selectedViewController
} else {
break
}
}
return top
}
}
This is the currentViewController that I am trying to push another controller over from the deeplink
#objc func addNavBarItemTapped() {
let storyBoard = UIStoryboard(storyboard: .addVC, bundle: .main)
let controller = storyBoard.instantiateViewController(withIdentifier: "AddViewController")
self.navigationController?.pushViewController(controller, animated: true)
}
How can I effectively push over the AddViewController because that is the presenting ViewController and when I dismiss the presented ViewController from the navigation stack, I am not removing the entire app navigation but instead returning back to AddViewController
How the dismissal of this view controller is achieved is like this.
public extension UIViewController {
func alert(title: String,
message: String? = nil ,
attributedMessage: NSMutableAttributedString? = nil,
okAction: AlertActionButton = ("ok_button".fpxLocalizedText, .default, nil),
cancelAction: AlertActionButton = (nil, .cancel, nil),
complete: (() -> Void)? = nil) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
if let attributedMessage = attributedMessage {
alertController.setValue(attributedMessage, forKey: "attributedMessage")
}
let oKAction = UIAlertAction(title: okAction.0, style: okAction.1, handler: okAction.2)
if let cancelButtonTitle = cancelAction.0 {
let cancelAction = UIAlertAction(title: cancelButtonTitle, style: cancelAction.1, handler: cancelAction.2)
alertController.addAction(cancelAction)
}
alertController.addAction(oKAction)
self.present(alertController, animated: true, completion: complete)
}
}
In the presented Viewcontroller
public func declineButtonClicked() {
alert(title: "decline_warning_title".fpxLocalizedText,
message: "decline_warning_message".fpxLocalizedText,
okAction: ("yes_button".fpxLocalizedText, .destructive, { _ in self.sendDeclineRequest() }),
cancelAction: ("decline_cancel_button".fpxLocalizedText, .cancel, { _ in self.dismiss(animated: true, completion: nil) }))
}
Also sometimes if I have a presented Viewcontroller and need to show this ViewController, it is often presented in the background of the presented viewcontroller
In iOs push duty perform with UINavigationController
wherever you need to push so you need to UINavigationController.
You should Create another UInavigationController in Deep Link,
let storyBoard = UIStoryboard(storyboard: .addVC, bundle: .main)
let controller = storyBoard.instantiateViewController(withIdentifier: "AddViewController")
let navigationController = UINavigationController(rootViewController:controller)
self.navigationController?.present(navigationController, animated: true, completion: nil)
or
window?.getCurrentNanvigationController().present(navigationController, animated: true, completion: nil)
so now you have a new navigation controller that work stand alone.
So for this project I am in I need to implement a login using QR code, but I am new to swift and I don't know how
Right now I am implementing the QR code which scans and gets a string shown, I want that string to be placed into the API ID variable which gets the list of cars in my case from the API call
This is the QRCodeViewController() class, a function which gets the string from the scan:
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
if metadataObjects != nil && metadataObjects.count != 0
{
if let object = metadataObjects[0] as? AVMetadataMachineReadableCodeObject
{
if object.type == AVMetadataObject.ObjectType.qr
{
let alert = UIAlertController()
alert.addAction(UIAlertAction(title: "Retake", style: .default, handler: nil))
alert.addAction(UIAlertAction(title: "Vazhdo", style: .default, handler: {_ in
let idfromQR = object.stringValue
let viewController = MonitorimiViewController()
viewController.id = idfromQR!
let controller = self.storyboard?.instantiateViewController(withIdentifier: "tabBarController")
controller?.modalTransitionStyle = .flipHorizontal
controller?.modalPresentationStyle = .fullScreen
self.present(controller!, animated: true, completion: nil)
}))
present(alert, animated: true, completion: nil)
}
}
}
}
Now I want that object. StringValue that comes from the QR scanned to be passed into the MonitorimiViewController() which is another class which hold the ID value that needs to get from the object. StringValue but I am struggling to pass that value because it isn't being parsed, I tried all I know but all failed to be parsed.
This is the MonitorimiViewController() class
So the object.stringValue must be passed in this var id: String = "object.stringValue"
var id: String = "0E79C6205FD04F8994B13F5255B7FB05"
The problem is in the following lines of code:
let idfromQR = object.stringValue
let viewController = MonitorimiViewController()
viewController.id = idfromQR!
let controller = self.storyboard?.instantiateViewController(withIdentifier: "tabBarController")
controller?.modalTransitionStyle = .flipHorizontal
controller?.modalPresentationStyle = .fullScreen
self.present(controller!, animated: true, completion: nil)
You are assigning the qr code response to a view controller that is never getting presented. So you should instead assign it to one of the tabbar controller's view controllers.
Replace the code above with this code to solve your problem:
guard let tabBarController = self.storyboard?.instantiateViewController(withIdentifier: "tabBarController") as? UITabBarController else {
// Something wrong with your identifier
return
}
(tabBarController.viewControllers[0] as? MonitorimiViewController)?.id = idfromQR!
tabBarController.modalTransitionStyle = .flipHorizontal
tabBarController.modalPresentationStyle = .fullScreen
self.present(tabBarController, animated: true, completion: nil)
This assumes that MonitorimiViewController is the first index view controller in your tab bar controller, if it is not just change the 0 index to another number.
You are creating a new viewController, passing the QR code value into it, then you do nothing with the viewController and it falls out of memory.
let idfromQR = object.stringValue
let viewController = MonitorimiViewController()
viewController.id = idfromQR
You don't do anything with viewController after this point. You create a tabBarController from a storyboard, which creates new copies of these classes and open those new copies.
You would need to do something like this instead:
guard let controller = self.storyboard?.instantiateViewController(withIdentifier: "tabBarController") as? UITabBarController,
let monitorController = controller.viewControllers.first as? MonitorimiViewController else {
return
}
monitorController.id = idfromQR
self.present(controller, animated: true, completion: nil)
What i'm doing here is:
Getting the tabBarController from the storyboard
Getting one of the viewControllers it manages (i'm grabbing the
first, I don't know what index yours is).
Checking that they can all be cast to the correct types.
Passing the value into the MonitorimiViewController being used by the tabBar
Presenting the tabBar with the updated MonitorimiViewController
Please also make sure to use if let xxx or guard let xxx statements and avoid force unwrapping (e.g. idfromQR!) at all costs. It will crash your app if a problem occurs
I am running into the issue of my viewcontrollers not showing up even though the function calling the viewcontrollers seem to be running. The error I receive in the console is:
Warning: Attempt to present on whose view is not in the window hierarchy!
I have tried the suggestions on the "Attempt to present UIViewController on UIViewController whose view is not in the window hierarchy" thread without any progress. I am running everything programmatically.
func handleNewPage(){
print("this code works")
let uid = Auth.auth().currentUser?.uid
ref = Database.database().reference()
let usersReference = ref.child("patient").child((uid)!)
if usersReference.child("Doctor Code") != nil {
func presentNewPage(){
let firstPage = LandingPage()
let navCon = UINavigationController(rootViewController: firstPage)
present(navCon, animated: true, completion: nil)
}
presentNewPage()
print("PRINT")
} else{
let newPage = PresentViewController() // doctor reg page
let navController = UINavigationController(rootViewController: newPage)
present(navController, animated: true, completion: nil)
}
}
The function is called and the print statements come out valid. Yet, the viewcontrollers will not appear.
You have to create presentNewPage() function outside of your if ???
if usersReference.child("Doctor Code") != nil {
func presentNewPage(){
let firstPage = LandingPage()
let navCon = UINavigationController(rootViewController: firstPage)
present(navCon, animated: true, completion: nil)
}
presentNewPage()
print("PRINT")
} else{
let newPage = PresentViewController() // doctor reg page
let navController = UINavigationController(rootViewController: newPage)
present(navController, animated: true, completion: nil)
}
change it like this
func presentNewPage(){
let firstPage = LandingPage()
let navCon = UINavigationController(rootViewController: firstPage)
present(navCon, animated: true, completion: nil)
}
func handleNewPage(){
print("this code works")
let uid = Auth.auth().currentUser?.uid
ref = Database.database().reference()
let usersReference = ref.child("patient").child((uid)!)
if usersReference.child("Doctor Code") != nil {
presentNewPage()
print("PRINT")
} else{
let newPage = PresentViewController() // doctor reg page
let navController = UINavigationController(rootViewController: newPage)
present(navController, animated: true, completion: nil)
}
}
I have a function, whereby I check if the UserDefaults are set and if not a new View Controller opens and presents a login screen which will set the user defaults.
My problem is the view controller does not Instantiate but I get a print "User not registered"
func checkUserAccount() {
let defaults = UserDefaults.standard
let accountName = defaults.bool(forKey: "PUserAccountName")
let accountPassword = defaults.bool(forKey: "PUserAccountPassword")
if accountName == true && accountPassword == true {
print("User Registered")
} else {
let storyboard: UIStoryboard = UIStoryboard(name: "PolTRiM", bundle: nil)
let vc: StudentLoginVC = storyboard.instantiateViewController(withIdentifier: "studentLogin") as! StudentLoginVC
vc.modalPresentationStyle = .custom
vc.modalTransitionStyle = .crossDissolve
self.present(vc, animated: true, completion: { _ in })
print("User not registered")
}
}
Any thoughts?
Have you double checked UIStoryBoard name and UIViewController identifier if it's written correctly? Otherwise this code is working for me
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier :"MyViewController") as! UIViewController
self.present(viewController, animated: true)
override init() {
super.init()
parseLoginHelper = ParseLoginHelper {[unowned self] user, error in
// Initialize the ParseLoginHelper with a callback
if let error = error {
// 1
ErrorHandling.defaultErrorHandler(error)
} else if let _ = user {
// if login was successful, display the TabBarController
// 2
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let tabBarController = storyboard.instantiateViewControllerWithIdentifier("TabBarController")
// 3
self.window?.rootViewController!.presentViewController(tabBarController, animated:true, completion:nil)
}
}
}
So I understand that the code here should be able to replace my login screen upon hitting login, however, it does not. Instead it loads and stays on the login screen for parse. However, if I exit the app, it loads the proper screen. Does anyone have any ideas how to dismiss login screen more efficiently upon login?
let user = PFUser.currentUser()
let startViewController: UIViewController;
if (user != nil) {
// 3
// if we have a user, set the TabBarController to be the initial view controller
let storyboard = UIStoryboard(name: "Main", bundle: nil)
startViewController = storyboard.instantiateViewControllerWithIdentifier("TabBarController") as! UITabBarController
} else {
// 4
// Otherwise set the LoginViewController to be the first
let loginViewController = PFLogInViewController()
loginViewController.fields = [.UsernameAndPassword, .LogInButton, .SignUpButton, .PasswordForgotten]
loginViewController.delegate = parseLoginHelper
//loginViewController.signUpController?.delegate = parseLoginHCelper
startViewController = loginViewController
}
// 5
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window?.rootViewController = startViewController;
self.window?.makeKeyAndVisible()
return false
}
I believe you should not call
self.window?.rootViewController!.presentViewController(tabBarController, animated:true, completion:nil)
instead if you already in some controller why just go to tabBar like that:
presentViewController(tabBarController, animated:true, completion:nil)
You should not use :
self.window?.rootViewController!.presentViewController(tabBarController, animated:true, completion:nil)
Try to change that line by :
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("tabBarController") as! tabBarController
self.presentViewController(vc, animated: true, completion: nil)
and in your storyboard, set the storyboard id of tabBarController under identity inspector to : tabBarController