Unwind from OverCurrentContext modal presentation doesn't work - ios

I am having a problem similar to this:
Cannot Get Unwind Segue Working with Modal View Controller Presented Over Current Context
I presented a VC modally using OverCurrentContext presentation style following a tutorial. All set in storyboard.
VC1 -> modally overCurrentContext -> VC2
The problem is that Xcode 8 has slightly different options in the Attributes Inspector, no OverCurrentContext option in the Segue, so I had to select the presentation: default in the Segue
and set the presentation: overCurrentContext in the VC2
Now, when I try to unwind back to the VC1 I can't, nothing happens and I get stuck in VC2.
I had to add some extra code to the unwind method to make this work.
#IBAction func unwindFromAdvancedOptions(segue: UIStoryboardSegue) {
if segue.source as? VC2 != nil {
segue.source.dismiss(animated: true, completion: nil)
}
}
But I'd really like to understand why storyboard settings where enough to unwind any other modal presentation styles but OverCurrentContext.

Related

How to 'pop' a navigation controller and all of its view controllers

I have inherited a navigation controller issue in an existing app that I'm trying to solve cleanly.
This app has multiple storyboards and multiple UINavigationControllers. At one point in the app, a series of view controllers is presented modally, using a separate storyboard and a separate nav controller. When the modal process is complete, the navigation hierarchy looks something like this:
NavController1 -> VC1 ['Present Modally' segue] -> NavController2 -> VC2 -> VC3 -> VC4
When the user completes the modal activity in VC4, dismiss() is called programmatically on VC4 and the user can then navigate back to VC1 using the back button.
However, what we really need to do is to 'pop off' all of the modally presented set of view controllers (and their nav controller) when the user finishes the modal activity. The problem is that from VC3 or VC4 I can't call popToRootViewController(). I also can't traverse down the VC stack to find VC1, since the current Nav controller doesn't manage it.
A couple solutions come to mind:
1) use the notification manager and have VC1 listen for the message to pop everything off back to itself
2) pass a reference to VC1 as a delegate all the way up the chain so that VC3 or 4 can have it pop everything off
Both of these solutions follow the general maxim that the presenting VC should be the one that dismisses, but neither are what I would consider clean.
I would welcome any thoughts or alternative solutions.
Assuming that these are the way the view controllers were laid out:
NavController1 --['Root View Controller' segue]--> VC1 --['Present Modally' segue]--
--> NavController2 --['Root View Controller' segue]--> VC2 --['Push' segue]--> VC3 --['Push' segue]--> VC4
You should be able to go back to VC1 by dismissing either VC2, VC3 or VC4.
// example for vc4
vc4.navigationController?.dismiss(animated: true, completion: nil)
However, if each of the viewControllers were presented modally, you should be able to traverse through the presentingViewController to reach VC1.
var currentVC: UIViewController? = self
var presentingVC: UIViewController? = currentVC?.presentingViewController
while presentingVC != nil && !(presentingVC is VC1) {
currentVC?.dismiss(animated: true, completion: nil)
currentVC = presentingVC
presentingVC = currentVC?.presentingViewController
}
Hope that helps.
When popping, you may kick out the viewControllers from your navigation Controller, would solve your problem
extension UINavigationController {
public func removeViewController(classes : [String]) {
var vcs = [UIViewControllers]()
for viewController in self.viewControllers {
let name = viewController.className
if !classes.contains(name) {
vcs.append(viewController)
}
}
if classes.count < vcs.count {
self.viewControllers = vcs
}
}
}
now think you have 4 viewControllers , A, B, C, D, you want to remove B and C and Move Back To A
In D's View Controller
override func viewDidLoad() {
super.viewDidLoad()
//your works
let viewControllersToRemove = [String(describing: type(of:B)), String(describing: type(of:C))]
navigationController.removeViewControoler(classes : viewControllersToRemove)
}

How to embed a UINavigationController in prepareForSegue?

Currently I have a VC1 of type UITableViewController that is embedded in UINavigationController. When the user selects a cell, it performs a push segue to VC2 also of type UITableViewController. That is fine because the navigation bar is still present.
However in VC1, there is a UIBarButtonItem in the navigation bar, that upon tapped, also segues to VC2 and performs different things, but it uses all the same menu layout.
Using the UIBarButtonItem to segue to VC2, I like to perform a modal segue to distinguish between the two actions.
I know that modal segues encompasses the whole screen, and any top bars will be removed.
I followed this question: Modal segue, navigation bar dissapears
One of the answers provided shows how to embed a UINavigationController in prepareForSegue, so I followed it and implemented:
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.identifier == "AddSegue"
{
let navigationController: UINavigationController = segue.destination as! UINavigationController
var addVC: VC2 = VC2()
addVC = navigationController.viewControllers[0] as! VC2
}
}
However, I get the error:
Could not cast value of type 'VC2' to
'UINavigationController'
How can I properly embed a UINavigationController in prepareForSegue because I need to pass data between different view controllers.
Here's my storyboard:
The segue you're intersecting is to your VC2, not a navigation controller. You're going to need to update the "AddSegue" segue to point to a navigation controller, then link your VC2 as the root view controller of the navigation controller.
If you post your storyboard, I can give you more details on how to do this.
EDIT: Here's how you should set up your storyboard. It might get a bit weird, since VC2 has two parent navigation controllers, but it should be fine.

ios swift - Presenting tab bar controller programatically [duplicate]

This question already has an answer here:
self.tabBarController dismissViewControllerAnimated doesn't work
(1 answer)
Closed 6 years ago.
I'm having a hard time presenting a tab bar controller that is not the root view controller.
I have the current setup:
I want to press a button in my main view controller and be able to present the tab bar controller with the option to go back to the main view controller.
I tried creating a class of type UITabBarViewController, associating it to my Tab Bar Controller and just presenting it but it does not work.
I would like to present the tab bar controller with the favorites tab selected.
What I tried:
let vc = TabBar()
self.presentViewController(vc, animated: true, completion: nil)
You can switch tabs by setting a selected index property of UITabBarController. Like this:
tabBarController.selectedIndex = 1
You don't need to create new view controllers or perform segues if all you want is to switch between the two tabs.
You can do it in tow manner :
using segue :
Drag from button to the tabBarViewController And choose a type (Modal, Push(if your mainViewController is NavBarVC) ...)
from code :
click on your tabBarViewController and go to the attributes inspector and give your VC a storyboard id
and from code :
let mainST = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
let VC = mainST.instantiateViewControllerWithIdentifier("idTabBar")
presentViewController(VC, animated: true, completion: nil)
Edit dismiss tabBar
if the tabBar is presented modally,
to dismiss it you have tow choices :
1) using a delegate :
protocol ExitMe {
func exitMe()
}
In the view controller presenter of the tabBar
extension PresenterOfTabBar: ExitMe{
func exitMe(){
dismissViewControllerAnimated(false, completion: nil)
}
}
and in the tabBarViewController define an exitDelegate variable var exitDelegate: ExitMe! and set it's value from the presenter. When the user click a button to exit tabBar you just call exitDelegate.exitMe()
using an unwindFuction when presenting modally using a segue:
in the presenter you define a function like this
#IBAction func unwindFromTabBar(sender: UIStoryboardSegue){
// do what you want here
}
and in the InterfaceBuilder drag from the the button that should exit the tabbar to the exit in the view controller dock then choose the func unwindFromTabBar.
Others solutions may exist (using notification, get the prsenter View controllers ....) you should pick the suitable one...

Remove view from RAM swift

I'm using segue navigation with self.perfomSegueWithIndentifier function with type of show segues.I noticed after every navigation other view/viewcontroller did not remove from RAM.What I have to do to solve this
This is what you are looking for:
On Button Tap on View Controller 1:
self.navigationController.pushViewController(secondViewController, animated: true)
On Button Tap on View Controller 2:
self.navigationController?.popViewControllerAnimated(true)
Programmatically
Lets say you have VC1 embed in UINavigationController for now VC1 becomes the top view controller on the navigation stack.
So if you are going VC1 -> VC2
self.navigationController?.pushViewController(vc2, animated: true)
In that case VC2 becomes the top view controller on the navigation stack.
and while coming back from VC2 -> VC1 (Button tap on VC2 perform this statement)
self.navigationController?.popViewControllerAnimated(true)
In that case again VC1 becomes the top view controller on the navigation stack.
Using Segue
Just use Show(e.g Push) and set the identifier for that like i have pushView
and invoke
self.performSegueWithIdentifier("pushView", sender: nil) for moving VC1 -> VC2
and for coming back to VC2 -> VC1
use the same self.navigationController?.popViewControllerAnimated(true)

Present modal view controller over modal view controller

I have a view controller VC1 that is presented modally over full screen from some other VC0. In my storyboard I have a modal segue from VC1 to VC2 also presenting over full screen. When in my app I can plainly see VC2 over VC1 over VC0, because some parts of their views are transparent. Perfect.
However, I'm going to reuse VC2 many times so I don't want to have a segue to it for each controller in my storyboard, so I want to accomplish this same thing programmatically. However, when I call presentViewController:animated:completion in VC1 to present VC2, the view of VC1 disappears when the modal transition is complete. When VC2 is dismissed, the view of VC1 reappears when the transition animation is complete.
How can I get the same effect programmatically as when I'm using the storyboard segue?
let newView = self.storyboard!.instantiateViewControllerWithIdentifier("NewViewController") as! NewViewController
newView.modalPresentationStyle = UIModalPresentationStyle.OverFullScreen
self.presentViewController(newView, animated: true, completion: nil)
You can only present on a visible controller, which is usually the rootViewController. But when there is a modally-presented controller, it covers the root controller, so you can't use that. But you can present on the modal, which is accessed though rootViewController.prsentedViewController. Here's some code:
let rootVC = window?.rootViewController
let presentingVC = (rootVC?.presentedViewController ?? rootVC)
presentingVC?.present(myController, animated: true, completion: nil)
You don't need to change the modalPresentationStyle.
You need to set the modalPresentationStyle property of the presented controller to UIModalPresentationOverFullScreen. Set that property just before you call presentViewController:animated:completion.

Resources