Dismiss ViewController presented modally when tab is changed - ios

I have a UITableViewControllerthat presents a UIViewController modally when didSelectRowAtis invoked.
My application is wrapped in a UITabBarController.
I would like to dismiss the UIViewController when a user changes tabs.
I have tried to call dismiss on my controller like so, but this does not work.
let vc = VimeoController()
....
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
vc.dismiss(animated: true) {
print("dismissed")
}
}
...
fileprivate func presentModal() -> Void {
vc.modalPresentationStyle = .overCurrentContext
present(vc, animated: true, completion: nil)
}

Place your dismiss call within the viewDidDisappear lifecycle hook of your VimeoController controller instead.
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
dismiss(animated: true, completion: nil)
}

Related

Present a modal view controller, but don't hide the navigation bar

I'm modally presenting a viewController with a semi-transparent view. It's a custom activity indicator. I would like it to cover the view, but leave the navigation bar and tab bar visible and accessible.
The docs, and several SO answers (e.g. Presenting a Modal View Controller hides the Navigation Bar) seem to suggest that presenting the modal onto the navigation controller should achieve this. But when I do it, it shows the tab bar correctly, but covers the navigation bar.
Any ideas? Here is the relevant code:
let spinnerVC = SpinnerViewController()
spinnerVC.modalPresentationStyle = .overCurrentContext
spinnerVC.modalTransitionStyle = .crossDissolve
self.navigationController?.present(spinnerVC, animated: true, completion: nil)
//self.navigationController is definitely not nil
You can present a your viewcontroller by adding as rootViewController of a navigationController and then present it over the current viewController like this:
let spinnerVC = SpinnerViewController()
let navVC = UINavigationController(rootViewController:spinnerVC)
navVC.modalPresentationStyle = .overCurrentContext
navVC.modalTransitionStyle = .crossDissolve
self.present(navVC, animated: true, completion: nil)
you could do it two ways:
first one:
Put this code in your parent view
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(true, animated: animated)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: animated)
}
second one:
Add a reference to your invoker in your alert controller in order to hide the bar like this:
weak var invokerView : UIViewController?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.invokerView?.navigationController?.setNavigationBarHidden(true, animated: animated)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.invokerView?.navigationController?.setNavigationBarHidden(false, animated: animated)
}
Don't present it. Add it as a child view controller to the top view controller of your navigation controller and add its view as a subView to the view of the same adjusting the frame.
let spinnerVC = SpinnerViewController()
spinnerVC.view.frame = self.navigationController?.topViewController?.view.bounds
self.navigationController?.topViewController?.addChildViewController(spinnerVC)
self.navigationController?.topViewController?.view.addSubview(spinnerVC.view)

segue back with NavigationBar

Hello I am in Viewcontroller B with its NavigationBar, button pressed in Viewcontroller B goes to Viewcontroller C (I don't want NavigationBar in Viewcontroller C)
let vc = self.storyboard?.instantiateViewController(withIdentifier: "viewC") as! ViewcontrollerC
vc.passAction = "saveedit"
vc.passName = passName
self.present(vc, animated: true, completion: nil)
When I click save Button in ViewcontrollerC I should go back to ViewcontrollerB with ViewcontrollerB NavigationBar
let vc = self.storyboard?.instantiateViewController(withIdentifier: "viewB") as! ViewcontrollerB
vc.passName = "\(firstNameTxt.text!)\(" ")\(lastNameTxt.text!)"
self.present(vc, animated: false, completion: nil)
Here the problem is When I go back to ViewcontrollerB I don't see NavigationBar there.
EDITED
class ViewcontrollerB : UpdateDataDelegate {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated);
self.navigationController?.isNavigationBarHidden = false
}
#IBAction func click_edit(_ sender: Any) {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "patientPersonalData") as! patientPersonalDataVC
vc.passName = passName
vc.passAction = "saveedit"
self.navigationController?.pushViewController(vc, animated: true)
}
func loadData() {
}
}
//ViewcontrollerC
protocol UpdateDataDelegate {
func loadData()
}
class Viewcontroller C {
var delegate: UpdateDataDelegate?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.isNavigationBarHidden = true
}
fun click_save() {
self.navigationController?.popViewController(animated: true)
self.delegate?.loadData()
}
}
It seems that you create a new ViewcontrollerB.
Try this to go back to ViewcontrollerB.
self.dismiss(animated: true, completion: nil)
Why don't you simply dismiss ViewcontrollerC on pressing Save button?
In ViewcontrollerC:
var closure: ((String)->())? //Set this closure when you present ViewcontrollerC from ViewcontrollerB
func save()
{
self.dismiss(animated: true) {[weak self] in
self?.closure?("Your_Data")
}
}
This will move you back to ViewControllerB where the navigation bar is already present.
You don't need to hide/show the navigation bar anywhere, neither while presenting ViewControllerC, nor when dismissing it.
Try to present it using navigation controller
let vc = self.storyboard!.instantiateViewController(withIdentifier: "viewC") as! ViewcontrollerC
let navController = UINavigationController(rootViewController: vc)
self.present(navController, animated:true, completion: nil)
and add the following line in your ViewcontrollerC's viewWillAppear method.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.isNavigationBarHidden = true
}
when leave from ViewcontrollerC's viewWillDisAppear method.
override func viewWillDisAppear(_ animated: Bool) {
super.viewWillDisAppear(animated)
self.navigationController?.isNavigationBarHidden = false
}
I just have posted code dedicated on UINavigationBar appearance management on github. check out RRViewControllerExtension, it will solve your problem gracefully.
with RRViewControllerExtension all you have to do is just override method in your viewcontroller.
//override any of the methods below in your viewcontroller's .m file to make specific navigation bar appearance
-(BOOL)prefersNavigationBarHidden;
-(BOOL)prefersNavigationBarTransparent;
-(nullable UIColor *)preferredNavatationBarColor;
-(nullable UIColor *)preferredNavigationItemColor;
-(nullable UIImage *)preferredNavigationBarBackgroundImage;
-(nullable NSDictionary *)preferredNavigationTitleTextAttributes;
Try this .. unhide navigationBar in viewWillAppear of viewControllerC and hide it in viewWillAppear of viewControllerB .
//ViewController C
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.isNavigationBarHidden = true
}
//ViewController B
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated);
self.navigationController?.isNavigationBarHidden = false
}
try this then
//ViewController C
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(true, animated: true)
}
//ViewController B
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated);
self.navigationController?.setNavigationBarHidden(false, animated: true)
}

can't update navigationbar when popping to root view controller

I have a UITabBar. In one tab is a UINavigationController. Let's say the 2nd or 3rd UIViewController in the stack has this:
class ChildVC: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.setNavigationBarHidden(true, animated: false)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
navigationController?.setNavigationBarHidden(false, animated: false)
}
}
If you click the current tab it will popToRootViewController() on the navigation controller. The problem is, in viewWillDisappear(:) of my current tab the navigationController is nil. So the navigationBar remains hidden.
What's the proper way to handle this? Should I just set the navigation bar to visible in the root view controller's viewDidAppear? That seems hacky.
If anybody else sees this, I don't know why the reference to self.navigationController gets set to nil before viewWillDisappear when you popToRootViewController() but a workaround I found was just to store your own reference to it.
class ChildVC: UIViewController {
private weak var navCtrl: UINavigationController?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navCtrl = navigationController
navCtrl?.setNavigationBarHidden(true, animated: false)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
navCtrl?.setNavigationBarHidden(false, animated: false)
}
}
You should override the viewWillAppear in the rootViewController and setNavigationBarHidden from there. navigationController is nil at viewDidDisappear because it has already been popped off the navigation stack.

Swift: Not able to dismiss modally presented LoginViewController

As SplitViewController loads, I am showing a Login Screen. On successful login, I need to go back to parent view controller. Somehow dismissal is not working for me. Here is the code:
ParentViewController:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if !appDelegate.loggedIn {
self.performSegueWithIdentifier("loginScreen", sender: self)
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
Child ViewController:
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.loggedIn = true
self.dismissViewControllerAnimated(true, completion: nil)
The dismissal part never works. It just hangs on Login Screen.
Try one of the following:
1) remove self. keep only dismissViewControllerAnimated(true, completion: nil)
or remove self. and make it:
2) presentingViewController.dismissViewControllerAnimated(true, completion: nil)
or remove self. and try:
3) presentedViewController.dismissViewControllerAnimated(true, completion: nil)
Try this in your parent view controller:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if !appDelegate.loggedIn {
let loginVC: UIViewController = self.storyboard!.instantiateViewControllerWithIdentifier("LoginViewController") as UIViewController
loginVC = UIModalTransitionStyle.CoverVertical
self.parentViewController?.presentViewController(loginVC, animated: true, completion: nil)
}
}
You're instantiating the new view controller by its own name rather than by the segue name.

How to dismiss current view controller and parentviewcontroller?

I am presenting two view controller like so
self.presentViewController(choosePlace, animated: true, completion: nil)
self.presentViewController(shareController, animated: true, completion: nil)
and I want to dismiss both of them like this:
println("parent \(self.parentViewController)")
self.dismissViewControllerAnimated(true, completion: {
self.parentViewController?.dismissViewControllerAnimated(true, completion: nil)
})
self.parentViewController is always nil though. How can I dismiss two at the same time?
You can:
self.dismissViewControllerAnimated(true, completion: {
self.presentingViewController?.dismissViewControllerAnimated(true, completion: nil)
})
So, make sure that you are presenting your view controllers in order:
parentViewController -> choosePlace -> shareController
(arrows indicate "self.presentViewController")
Swift 4
There where some issue I faced while trying Michael Voline's answer.
As the comment on the answer it did not work for me. it was because of the presentingViewController was nil.
So we need to set in a different property say presentingController but this will not work if you are setting it on the viewDidLoad
private var presentingController: UIViewController?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
presentingController = presentingViewController
}
then
dismiss(animated: false, completion: {
self.presentingController?.dismiss(animated: false)
})
I made the animation false so that the user will not see the UIof presenting view controller in the split seconds of dismissing.
You can reference the presenting view controller during the function 'viewWillDisappear' of your view controller (i.e ShareController dismisses choosePlace just before it leaves scope).
//place the below in shareController
override func viewWillDisappear(_ animated: Bool) {
self.presentingViewController?.dismiss(animated: false, completion: nil)
}
If Michael Voline's solution doesn't work , try the following code.
let customViewController = self.presentingViewController as? CustomViewController
self.dismiss(animated: true) {
customViewController?.dismiss(animated: true, completion: nil)
}

Resources