In my iOS application, my VC is presenting a modal (A) via code.
However, when I already have another modal presented (B), this one is not showing at all.
However, when (A) is deinitted, I see that (B) also gets deinitted.
How can I make sure that (B) always gets shown, no matter what, and in front of all other modals?
performSegueWithIdentifier(SEGUES.SegueTabBarToBroadcast, sender: view )
My TabBarViewController is calling this segue. (The segue is modal according to storyboard).
The problem occurs when one of the view controllers in my TabBar presents a modal. Then, when I try to call performSegueWithIdentifier, the modal doesn't show (but yet deinits when I close the other modal).
I just want this modal to present NO MATTER WHAT. This modal should overlap all other modals.
I also tried this, but the problem persists:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let bvc = storyboard.instantiateViewControllerWithIdentifier("BroadcastViewController") as! BroadcastViewController
self.presentViewController( bvc , animated: true, completion: nil)
Presenting multiple view controllers as a modal is not good practice. If you want to present your vc no matter what, then you have to understand the hierarchy of your view controllers. Shortly, you can present view controller modally on the vc with view that has a window, and when you have already presented any vc modally, your view controller's view that has presented the vc does not have the window, thus can't present other view controller modally. Conclusion: you can present vc modally from the top-most vc. So, solution would be keeping reference to the top-most vc, and presenting the desired vc from that vc. Another solution would be adding vc's view directly to the main window of the app, but I would not recommend solving your problem that way. Hope, this helps.
Related
i have multiple storyboard in my storyboard. I have embedded the firstVC with navigationcontroller and remaining attached to the firstvc through segues. The remaining VC's are not embedded in navigationcontroller. Now when i click the button to open the next VC without navigation controller. It opens it twice. When i disconnect the segue it does not open the VC. I'm confused that why this is happening so. This is how my UI Looks,
This is how i perform the segue,
self.performSegue(withIdentifier: "GoToNextVC", sender: self)
View Controllers operate on a stack hierarchy. By embedding your first view controller in a UINavigationController and setting that as the Initial View Controller, you're making it the root view controller for the whole stack. The issue you are experiencing is occurring because you are segueing to view controllers without the root view controller's knowledge every time you call performSegue(withIdentifier:sender:). This causes the stack to be out of order and disjointed, preventing you from returning to the previous view controller(s) when calling dismiss(animated:completion:).
Instead of segues, try pushing each view controller to the stack with:
navigationController?.pushViewController(VIEW_CONTROLLER, animated: true)
To use this:
Remove any segues in Interface Builder, they are no longer needed.
Replace VIEW_CONTROLLER with the view controller you want to push.
I have two UIViewController. First one is a welcome screen and second one is a login screen (which is inside a navigation Controller). Users can go back to welcome screen from login screen with a back button so login screen opens with self.present(LoginViewController(),animated: false) and after Login Screen, final UIViewController opens with appDelegate.window?.rootViewController = FinalViewController().
My problem is that neither LoginViewController or WelcomeViewController deinit at this scenario. However, If I;
Open FinalViewController (via changing RootViewController) directly from WelcomeViewController, without showing LoginViewController.
Open LoginViewController without showing WelcomeViewController then open FinalViewController (again changing RootViewController)
Controllers deinited. So I don't think any of viewcontroller has a retain cycle vs..
I want to deinit both login and welcome screens after open final controller.
EDIT: I found that putting it inside NavigationController blocks the deniting.
EDIT2: If I call self.dismiss(animated: false, completion: nil) before changing rootViewController. All controllers seems to be deinited but I'm not sure If It will be a better answer.
Why don't you use this hierarchy:
-UIWindow
-----UIWindow.RootViewController
----------UINavigationController
---------------WelcomeScreen
---------------LoginScreen (Push without animation)
On Login Success:
-UIWindow
-----UIWindow.RootViewController
----------UINavigationController
---------------FinalViewController
Hide navigation bar and use animated property as per need.
EDIT
A part from document:
viewControllerToPresent
The view controller to display over the current view controller’s content.
So reference of the parent controller can be access from presented controller, hence both VCs can access each other. e.g., self.presentedViewController. In order to remove it, one must dismiss controller. So presented controller will release the reference of presenter controller.
I got an application with a navigation view controller as Initial View Controller. After loading the initial view controller, I set up a notification listener. The Notification can be posted everywhere in the app. I have some pushed vc's and also modally presented. My goal is to return to the initial vc and present a modal view controller from there if the notification is triggered but I have no Idea how to do that. Do I need to do this outside of the MainViewController?
Answer assumes rootViewController is UINavigationController as specified by OP in his question
You can achieve what you want using
(UIApplication.shared.keyWindow?.rootViewController as! UINavigationController).dismiss(animated: true) {
(UIApplication.shared.keyWindow?.rootViewController as! UINavigationController).popToRootViewController(animated: true)
}
Whats happening is pretty simple. Knowing that your initial viewCOntroller is always UINavigationController, initially check if you have anything presented on rootView controller, if yes dismiss it and in the completion block pop to rootViewController of your initial viewController.
Hope it helps
I am working on some old code that I didn't write, and it's really not architected well...
The situation is that a view controller presents a custom view controller modally, however every 30 seconds the presenting view controller is recreated.
The issue here is that if the modal is on screen when this happens, then any effort to dismiss it results in odd behaviour (such as a white screen).
I have tried calling [self.presentedViewController dismissViewControllerAnimated]; on the newly recreated controller, but presentedViewController is nil as you would expect.
I have also tried keeping a weak reference to the modal view controller, then when the presenting VC is reloaded, setting this value to that of the old VC. This has allowed me to call self.customModalVC dismissViewControllerAnimated]; but this is causing the aforementioned white screen, perhaps because it's presenting VC is no longer in the stack?
Any and all suggestions appreciated.
Try passing the navigation controller to newly presented ViewController:
presentedVC.navigation = self.navigationController
Add this to newly created one for dismissing
self.dismiss(animated: false) {
_ = self.navigation?.popViewController(animated: true)
}
I have a UIImagePickerController presented on screen. When I choose the photo I need to dismiss the PickerController then immediately present another PhotoEditController. Here is my code
picker.dismissViewControllerAnimated(false, completion: {
self.presentViewController(editPhotoVC, animated: false, completion: nil)
})
There is a 0.1s flash between dismissing old VC and presenting new VC so the presentingViewController (self) is shown. How do I avoid that in an elegant solution not hacking it through? Thanks
The standard way of implementation is to dismiss first view controller (VC) with animation and present second VC with animation.
However, depending on your view hierarchy, you could also have your second VC loaded first and then present first VC on top of it. With this simply dismissing first VC without animation should show underneath second VC without delay.
Third approach, as LLooggaann suggested, don't dismiss first VC and simply present second VC. Once done, dismiss the entire view controller hierarchy in one shot.
You could instantiate PhotoEditController's instance, then do something like this:
instance.willMoveToParentViewController(controllerThatPresentsPicker)
controllerThatPresentsPicker.addChildViewController(instance)
controllerThatPresentsPicker.view.insertSubview(instance.view, belowSubview: picker.view)
instance.didMoveToParentViewController(controllerThatPresentsPicker)
It looks a bit like a hack though
That is most probably because dismiss is sent to the VC that presented the picker and it dismisses the picker modal vc and executes the completion handler after attempting to present the parent VC which is itself.
one solution would be creating your own transition class, you can basically copy paste the code from here to do that