Below is a hierarchy of my navigation controller
MainViewController
|
|
DetailViewController
Then I do the following on DetailViewController
[self presentViewController:reminderController animated:YES completion:nil];
After navigating to ReminderViewController, at some points I do
[self dismissViewControllerAnimated:YES completion:nil];
However, it brings me back to MainViewController instead of DetailViewController
That is weird. Any thoughts about this issue...
EDITED :
The reason I do presentViewController: reminderController animated: completion: on DetailViewController because reminderController is used to send a reminder. Just like goole app or other apps, when sending sth , we are using presentViewController.
Here is a sample of the documentation regarding the UIViewController class:
The presenting view controller is responsible for dismissing the view controller it presented. If you call this method on the presented view controller itself, it automatically forwards the message to the presenting view controller.
If you present several view controllers in succession, thus building a stack of presented view controllers, calling this method on a view controller lower in the stack dismisses its immediate child view controller and all view controllers above that child on the stack. When this happens, only the top-most view is dismissed in an animated fashion; any intermediate view controllers are simply removed from the stack. The top-most view is dismissed using its modal transition style, which may differ from the styles used by other view controllers lower in the stack.
Thus, I think you should first use a segue to push your DetailViewController, and then present the reminderController modally, which you'll be later able to dismiss using dismissViewControllerAnimated:completion: without dismissing DetailViewController.
Instead of using dismissViewController:animated: use
[self.navigationController popViewControllerAnimated:YES];
dismissViewController:animated removes all the UIViewControllers in it.
You will save you a lot of trouble if you read the UIViewController and UINavigationController references. Twice ;)
See this post for more details.
Related
I am trying to achieve a navigation similar to the Netflix app for iOS. When you click on a movie, a modal window pops up with a close button. If within this movie I choose to see another movie then the second modal pops up and in addition to the close button, a back button appears. I can use the back button to dismiss one by one and the close button to return to the base screen.
I am able to dismiss a single view using
dismiss(animated: true, completion: nil)
but how can I return to the base screen closing all modals at once? Also, is modals the way to go? I chose this because I didn't want the navigation bar on top.
I'm working with Swift 4.2 in Xcode 10.
The way you are dismissing a ViewController is not the correct way. The presenting view controller is responsible for dismissing the view controller. Ideally you have to implement a protocol in your presenting ViewController and , dismiss your modal from your 'presenting' ViewController not 'presented' ViewController.
The reason why your way still works is, when a ViewController calls self.dimiss if there's nothing to dismiss UIKit will delegate it back to its parent. If you implement this correct way, once you dismiss , your presenting viewcontroller will dismiss , hence all the presented viewcontrollers will be dismissed instead of the last one.
From Apple Docs:
The presenting view controller is responsible for dismissing the view controller it presented. If you call this method on the presented view controller itself, UIKit asks the presenting view controller to handle the dismissal.
If you present several view controllers in succession, thus building a stack of presented view controllers, calling this method on a view controller lower in the stack dismisses its immediate child view controller and all view controllers above that child on the stack. When this happens, only the top-most view is dismissed in an animated fashion; any intermediate view controllers are simply removed from the stack. The top-most view is dismissed using its modal transition style, which may differ from the styles used by other view controllers lower in the stack.
If you want to retain a reference to the view controller's presented view controller, get the value in the presentedViewController property before calling this method.
The completion handler is called after the viewDidDisappear(_:) method is called on the presented view controller.
try this
self.navigationController?.viewControllers.removeAll(where: {$0.isModalInPopover})
The initial view controller on my app is a UITabBarController that displays for logged in users.
For new users, however, my app delegate will point them to a login/registration view controller first:
// New user, show login
self.window.rootViewController = [self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:#"LoginViewController"];
When the user has completed the login/registration, I then send the user to the tabbar as such:
// Login done, go to main view
[self performSegueWithIdentifier:#"userLoginToMainSeg" sender:self];
However, doing it this way, the LoginViewController is not released (dealloc is not called).
Can someone explain the error in my logic here?
Your modal segue is basically doing:
[loginViewController presentViewController:mainViewController animated:YES completion: ...];
What this means is that mainViewController becomes loginViewControllers presentedViewController:
loginViewController.presentedViewController == mainViewController
mainViewController.presentingViewController == loginViewController
When you're presenting a view controller, the presenting view controller remains in the view controller hierarchy, so that you can later navigate back by calling:
[loginViewController dismissViewControllerAnimated: ...];
So it's perfectly normal that loginViewController is not released, since it's still the window's rootViewController. It's only that loginViewController is obstructed by the presented mainViewController.
If you want to eradicate loginViewController you can set window.rootViewController directly, but that wouldn't animate the transition. You can achieve animation by messing around the view controllers' views, but it's kind of outside the officially sanctioned territory...
IMO the cleanest solution would be to implement a basic container view controller that would be your window's rootViewController, and that could orchestrate the transition between loginViewController and mainViewController by animating their views, and then throwing away loginViewController. It would be kind of a primitive navigation controller without a navigation bar and a navigation stack – just swapping the current view controller with the new one, and throwing away the former.
I have a UIViewController that's embedded in a Navigation View Controller. I then modally present another view controller that shows a countdown. Once the countdown ends, the modal view controller should be dismissed and show a different underlying view controller from the original presenting UIViewController.
Does anyone know how to do this in ios8 with Swift?
There are different ways to approach this. One way is to replace the initial presenting view controller with the desired underlying one when you present the modal view controller.
NSArray * viewControllers = [self.navigationController viewControllers];
[viewControllers replaceObjectAtIndex:viewControllers.count - 1 withObject:replacementController];
Dismissing the modal will simply show the different underlying view controller that was already swapped.
when you do popViewController from stack there is a handler. This will be called at the end of this function so you can pass a block where you can presentViewController or make change to the current one.
I have a tab view controller with a navigation controller. In the first tab item I click on a button in a view that pops up a view with animated: YES.
Then when that view is done I hit another button that dismisses it. Like:
[self dismissViewControllerAnimated:NO completion:^{
ProfilesViewController *profile = [[ProfilesViewController alloc] init];
[self.navigationController pushViewController:profile animated:YES];
//SHOW YOUR NEW VIEW CONTROLLER HERE!
}];
But everytime this code runs, it dismisses the view, DOES NOT push the profiles controller, and shows the view from the first tab bar item.
How do I push the ProfilesViewController to the screen with a Back arrow?
If you are using dismissViewControllerAnimated to dismiss that means that the VC is presented modally. As such, it doesn't have a navigation controller (so self.navigationController is nil) and thus it can't push anything into the navigation controller.
You should really add a property to the controller which is a delegate or a completion block which can be used to push the controller from another controller (the one that presents it) to dismiss and push the controller.
A second option is to pass the navigation controller, it's a similar amount of code to using a block but not so good.
A crappy option is to use the parentViewController to find the appropriate navigation controller, but that sucks for many reasons.
I am working with UISplitViewController, and I want to present a VC from the detail view controller of the splitViewController. I do it like this:
[self presentModalViewController:VC animated:YES];
also tried this from master VC.
[self.detailVC presentModalViewController:VC animated:YES];
It comes for the full screen I mean it is like presenting from the UISplitViewController itself. I tried changing the modal presentation styles even though I got the same results.
What I want is the VC that is presented should be presented within the bounds of the DetailVC and it can be of DetailVC's entire frame, but not come anywhere near to the MasterVC.
For now I am using UIView Animation to achieve this, any ideas on how to do this just by presenting?
As of Xcode 6:
In the storyboard, select the present modally segue, and go to the Identity Inspector, and choose Current Context for the Presentation option.
Your detail view controller should be inside a navigation controller, then you push your additional view controller into the navigation controller instead of modally.