Window hierarchy ios - ios

I have a question that probably already has been answered but I just can't find it.
I have a view that calls some webservice and when the result comes back I want to open another view to present the result. Right after the call to WS I close that window.
Now my problem is that when I try to open it with presentViewController with
UIApplication.sharedApplication().keyWindow?.visibleViewController!.presentViewController or
UIApplication.sharedApplication().keyWindow?.visibleViewController!.childViewControllers[0].presentViewController
the same is if I access to rootViewControler.
I get the error that the view is not in the hierarchy any more since it already called viewWillDisappear.
So is there any way to show a view over all views no matter what is the current top view?
EDIT:
The solution was really ugly and stupid. I have a static variable which gets changed every time viewDidApear is called to that view. This way I have the last view that is shown. I know this isn't good way to do it or maybe even not safe way, but I don't have any other idea.

Yes, there is a way to show ViewController on top of another, but for this you should not touch your window at all.
In your view controller class right after your call completed call
let viewController = ViewController() //change this to your view controller class that you want to present
self.presentViewController(viewController, animated: true, completion: nil)

Call web service and navigate your view controller in main thread using below code.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// do some background task
dispatch_async(dispatch_get_main_queue(), ^{
// update UI here
// navigate your view controllers
let vc: UIViewController = (self.storyboard?.instantiateViewControllerWithIdentifier("viewContIdentifire"))!
self.presentViewController(vc, animated: true, completion: nil)
// **OR**
self.navigationController?.pushViewController(vc, animated: true)
});
);
Navigate back to VC in more convenient way using
// using this navigate your app to root view of ui stack
self.navigationController?.popToRootViewControllerAnimated(true)
// using this line of code navigate to any view
self.navigationController?.popToViewController(viewContName, animated: true)
// this navigate back to last view
self.navigationController?.popViewControllerAnimated(true)

Related

InAppSettingsKit Done button does not show up on view controller

I am trying to use InAppSettingsKit from my Swift app (via Swift package dependency to version 3.3.3), and I would like to be able to use the settingsViewControllerDidEnd delegate callback to determine when the user has dismissed the settings dialog, so that I can check for certain conditions that may require additional actions on the user's part.
The Done button was showing up if I pushed the view controller onto a navigation controller, but the code indicates that this method will not fire the Done button delegate callback, so I have been trying to use the present method to show the view controller.
Here is the code that I am using to instantiate and present the settings view controller:
func authenticationSettings(alert: UIAlertAction!) {
let viewController = IASKAppSettingsViewController()
viewController.delegate = self
self.present(viewController, animated: true, completion: nil)
}
And here is what I get, notice no Done button:
I have tried this card method of presenting, and also the full screen method, with no avail.
I tried stepping into the Objective-C code and and from what I could tell, the UIBarButtonItem navigation item was being created and added. Anyone have any ideas on what to try next?
As you may have noticed in the source code, UIBarButtonItem gets added on navigationItem. This item is used only if view controller is part of a navigation controller stack
When you're presenting a new view controller modally it doesn't have a navigation controller in the stack, so to make it work you need to wrap your controller with a UINavigationController:
func authenticationSettings(alert: UIAlertAction!) {
let viewController = IASKAppSettingsViewController()
viewController.delegate = self
let navigationController = UINavigationController(rootViewController: viewController)
self.present(navigationController, animated: true, completion: nil)
}

Swift cancel button to different controllers

I'm coding a simple app with swift and I'm stuck at the following point, I have two Controllers that lead to another one, and when I click on the cancel button, it always lead to the root Controller, no matter from where I come.
I have a first controller (UIViewController), that go to the Navigation Controller of my target Controller (the one from which I would like to go back to the right calling Controller).
I have a second controller (UITableViewController), which go directly to my target Controller.
Here's the code of my Cancel button:
// MARK: - Navigation
#IBAction func lendingCancelButton(_ sender: UIBarButtonItem) {
// Depending on style of presentation (modal or push presentation), this view controller needs to be dismissed in two different ways
let isPresentingInAddLendingMode = presentingViewController is UINavigationController
if isPresentingInAddLendingMode {
dismiss(animated: true, completion: nil)
} else if let owningNavigationController = navigationController {
owningNavigationController.popViewController(animated: true)
} else {
fatalError("the LendingViewController is not inside a navigation controller.")
}
}
If I correctly understood (you could then correct me if I'm wrong, I would learn something), it's testing if the ViewController that's presenting my target ViewController is a NavigationController.
So maybe that, as the second Controller (my UITableViewController) is not going through a NavigationController, so the last one calling my target view with a NavigationController is always the UIViewController.
Don't hesitate to tell me if it's not clear enough (too many times the word "Controller" in my post) or if you need additional code.
Try something like this
if let navigationController = presentingViewController as UINavigationController {
navigationController.popViewController(animated: true)
} else if let viewController = presentingViewController as UIViewController {
dismiss(animated: true, completion: nil)
} else {
fatalError("the LendingViewController is not inside a navigation controller.")
}
If i understood you want to use dismiss when you find a UIViewController and to pop the navigation when you find a UINavigationController right?
Ok so I finally found a way to make it working.
My tableViewController was embedded into a NavigationController. I removed it (since I could do without it, according to my need). From this View Controller, I draw a segue that "Show" my target view.
From my other ViewController (this one is embedded into a NavigationController), I draw a segue put that present modally my target view.
With the code provided in my initial post, it's working.
The only thing I didn't understand is why the NavigationController from my TableViewController was likely to cause it not working properly.

testing "presentingViewController is UIViewController" works fine in one case, fails in other

Following Apple's documentation for adding and editing information Apple guide here I have a Viewcontroller with a tableview. The tableview contains a header with a "Add new" Button. If a table row is selected the detailViewController is pushed onto the stack. The detailViewController is also embedded in a UINavigationController, as in Apple's docs. If "Add new" is pressed, another segue is performed which presents the UINavigationController modally, which in turns shows the detailViewController. This works fine and the animation clearly shows a modally presented ViewController.
The detailViewController contains a Cancel Button in the NavigationBar. If it is pressed the following code is run:
#IBAction func cancel(_ sender: UIBarButtonItem) {
// Depending on style of presentation (modal or push presentation), this view controller needs to be dismissed in two different ways.
var isPresentingInAddActionMode = false
if let presentingVC = self.presentingViewController{
isPresentingInAddActionMode = presentingVC is UINavigationController
}
streekgidsModel.undoManager.endUndoGrouping()
print("undo grouping ended and undone")
streekgidsModel.undoManager.undo()
if isPresentingInAddActionMode {
dismiss(animated: true, completion: nil)
}
else if let owningNavigationController = navigationController{
owningNavigationController.popViewController(animated: true)
}
else {
fatalError("The MealViewController is not inside a navigation controller.")
}
}
The first if-statement checks if the property presentingViewController is present, and if so if it is of type UINavigationController. If so, the viewController is presented modally and should be dismissed. If not it is pushed onto the stack and the owningNavigationController should pop the detailViewController.
Running this code does not work as described by Apple. The check on the presentingViewController shows it is present, but the type check gives back "invalid". This is treated as false. The test on the owningNavigationController succeeds (I think it should fail) and the popViewController is executed. As there was no push, the view controller is not popped or dismissed and is still visible. A second press on Cancel executes the func cancel again, which results in an error as there is no longer a group started in the undo manager.
Baffling thing is that I have the same code in another viewcontroller, with similar UIViewTable and navigation and it works fine.
So to frame the question: why does this not work the way Apple describes it, why does my other view controller work as it is supposed to? Any input is appreciated.
BTW, the fatal error text is straight from the docs so the naming is not relevant and it is never executed.
I would start with checking who is presenter.
According to Apple docs on this:
When you present a view controller modally (either explicitly or implicitly) using the present(_:animated:completion:) method, the view controller that was presented has this property set to the view controller that presented it. If the view controller was not presented modally, but one of its ancestors was, this property contains the view controller that presented the ancestor. If neither the current view controller or any of its ancestors were presented modally, the value in this property is nil.
If the docs are correct then your presenter should be your "Viewcontroller with a tableview" which, I guess, is not UINavigationController. If that is the case then you should understand why your code fails.
It depends on your context of course, but I would just simplify a check this way:
var isPresentingInAddActionMode = self.presentingViewController != nil
... // your other code
if isPresentingInAddActionMode {
dismiss(animated: true, completion: nil)
}
else if let owningNavigationController = navigationController{
owningNavigationController.popViewController(animated: true)
}
If I understood your question and intent correctly then it doesn't matter for you who (which class) presented your detailVC and you care only about how your detailVC was presented - either pushed in navigation view controller or presented modally. I think just by checking presentingViewController property you can get that information.

Previous View Controller is seen under the next view controller

I have a second view controller which segues from the previous one. How can I adjust the opacity of my SecondViewController so that I may still see the previous view controller under it. Thanks
To present a view modally and still be able to see the presenting view controller under it. Present your SecondViewController in code like this:
let vc = secondViewController
secondViewController.modalPresentationStyle = UIModalPresentationStyle.OverFullScreen
self.presentViewController(vc, animated: true, completion: nil)
Using this, ensure your secondViewController primary view has an alpha value less than 1.0 so you will be able to see through it. Also, the animated parameter can be true or false, it shouldn't affect the result.
Using the modalPresentationStyle accomplish what you are asking.
Just unclick/click Animates option through Interface Builder based on your requirement,

Change view controller when user exits app

Hey I am trying to make it so whenever the user presses the home button and then try to returns to my app, they are returned to the initial view controller. Is there anything I can write into the app delegate to get this to work?
I have tried to call this function in the applicationDidBecomeActive function:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let VC = storyboard.instantiateViewControllerWithIdentifier("homeVC") as! UIViewController
self.window?.rootViewController?.presentViewController(VC, animated: true, completion: nil)
But i get an error saying the view is not in the hierarchy.
Any help will be appreciated!
You want to implement the applicationDidBecomeActive method of your App's delegate, and programmatically perform a segue or dismiss the current view controller, depending on the navigation setup.
Your users might not like forced navigation though. The general assumption is that they can resume where they left off.
Well, every time the app is closed applicationDidEnterBackground in your AppDelegate is called. You can use that to present your view controller.

Resources