how to make a unwindsegue to viewcontroller before a navigation controller - ios

i have a project where initial viewcontroller does not have a navigation controller a loguin windows after that i have a navigatino controller to the main view. so when I tap on close session i make a segue to loguin windows but that does not destroy any windows opened in previous, so I google for a solution an i show a unwindsegue to main viewviewcontroller but always the initial view controller is behind a navigation controller so my real question is is a valid approach to make
#IBAction func unwindToVC1(segue:UIStoryboardSegue) {
self.performSegue(withIdentifier: "goLogin", sender: nil)
}

You need change sender value nil to self value:
#IBAction func unwindToVC1(segue:UIStoryboardSegue) {
self.performSegue(withIdentifier: "goLogin", sender: self)
}

Related

Having trouble "reaching" my override prepare function to programmatically change views in a tab bar controller

I have a view controller with a container view that has a tab bar controller embedded in it. Im using this to display 3 different view controllers based on what is pressed from a segmented control in the main vc. I have been following this solution: https://stackoverflow.com/a/38283669/11536234
My problem is that when I change the segmented control index (by pressing a different segment) I can't figure out how to "reach" the prepare function.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
print("prepare reached")
//super.prepare(for: segue, sender: sender)
//switch(segue.identifier ?? "") {
//case "TabBar":
guard let TabController = segue.destination as? UITabBarController else {
fatalError("Unexpected destination: \(segue.destination)")
}
TabController.selectedIndex = toggle.selectedSegmentIndex
//default:
//fatalError("Unexpected Segue Identifier; \(segue.identifier)")
//}
}
#IBAction func toggleAction(_ sender: UISegmentedControl) {
print("toggle is now at index: ", toggle.selectedSegmentIndex)
//performSegue(withIdentifier: "TabBar", sender: sender)
//container.bringSubview(toFront: views[sender.selectedSegmentIndex])
}
So far i have tried placing a performsegue function in an action function linked to the segmented control. This doesn't work, however, because it essentially adds another embedded tab bar programmatically or calls the embed segue again and I receive this error statement: "There are unexpected subviews in the container view. Perhaps the embed segue has already fired once or a subview was added programmatically?"
*The commented lines of code are there to show what I've tried that hasn't worked vs where I'm at.
When you embed a view controller to a container view from another view controller(MainVC), the segue is performed only once when MainVC loads. To pass values to the embedded UIViewController/UITabBarController, you need to get the child view controller and send the data
class MainVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func segmentControlAction(_ sender: UISegmentedControl) {
if let tabBarVC = self.children.first(where: { $0 is UITabBarController }) as? UITabBarController {
tabBarVC.selectedIndex = sender.selectedSegmentIndex
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//called before mainvc viewDidLoad
let destinationVC = segue.destination as? UITabBarController
destinationVC?.selectedIndex = 1
}
}
You can't do what you are trying to do.
With embed segues the segue fires when the host view controller is first loads. That invokes your prepare(for:sender) method, once and only once, before the embedded view controller's views are loaded. It doesn't get called again.
What you need to do is to save a pointer to your child view controller in an instance variable. Define a protocol that the parent uses to talk to the child, and then use that protocol to send a message to the child when the user selects a different segment in your segmented control.
Then the child (presumably the tab bar controller) can switch selected tabs in response to the message you define.

iOS Swift 4 Restart Navigation controller

My storyboard looks like this:
Navigation Controller -> StepOne -> StepTwo -> StepThree
StepOne has a Segue Show to StepTwo etc.
In StepOne I open StepTwo on Button click like this:
#IBAction func next(_ sender: UIButton) {
self.performSegue(withIdentifier: "oneToTwo", sender: self)
}
In StepTwo I open StepThree the same way.
Now In my last StepThree I want to restart at StepOne :
#IBAction func end_click(_ sender: UIButton) {
self.navigationController?.popToRootViewController(animated: false)
}
This works fine.
The problem is that all the UI elements are still filled out from the first run. Of course I could clear all values before I navigate in each controller but this doesn't seem to be good practice.
Is there a way to tell NavigationController to use new instances of its ViewController s ?
I guess you can simply dismiss the root view controller of navigation controller and present it again.
You need
// this inside end_click
let vc = self.storyboard!.instantiateViewController(withIdentifier:"StepOneId")
self.navigationController!.setViewControllers([vc], animated: true)

3 ways of view controller transition

i am a beginner in iOS development, and recently, i just follow along the tutorial for beginners.
let say i want to move from one VC to another VC by clicking a button, so i just find out that there are three ways to move from one ViewController to another ViewController (modal segue).
in main storyboard, i just click control and drag from the button to th destination view controller and choose present modally
programmaticaly, by implementing the code below
#IBAction func logInButtonDidPressed(_ sender: Any) {
// modal transition to VC2
let viewController2 =
storyboard?.instantiateViewController(withIdentifier:
"ViewController2") as! ViewController2
present(viewController2, animated: true, completion: nil)
}
programatically,by using perform segue function
#IBAction func logInButtonDidPressed(_ sender: Any) {
performSegue(withIdentifier: "toSecondViewController", sender: self)
}
are they just the same ? or is it used for different cases?
Thanks in advance :)
Yes, they are similar. And the obvious difference I think is the data passing. The first and third one are same, use the following method to pass data to next controller:
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
if let viewController2 = segue.destination as? ViewController2 {
viewController2.someProperty = someValue
}
}
For second transition, you directly set the data when creating the next controller:
let viewController2 = storyboard?.instantiateViewController(withIdentifier:
"ViewController2") as! ViewController2
viewController2.someProperty = someValue
present(viewController2, animated: true, completion: nil)
I would use segues, as there are some advantages compared to manual presentation:
You can create unwind segues to exit the current view controller to any view controller in the hierarchy.
You can add 3D touch support to segues with one mouse click.
The first and last method produce identical results. I would create segues with clicking and dragging whenever possible. If you need to do some data validation or other stuff before performing a transition, you have to call the performSegue method manually.

How to perform segue with navigation controller?

I just want to perform segue with a back button on top.
#IBAction func onFirst(_ sender: UIButton) {
performSegue(withIdentifier: "firstSegue", sender: self)
}
#IBAction func onSecond(_ sender: UIButton) {
performSegue(withIdentifier: "secondSegue", sender: self)
}
Let's say you have two view controllers, ViewControllerOne and ViewControllerTwo.
You could do that programmatically or using storyboards.
Since you seem to be using #IBOutlet I will assume you want to do it using storyboards.
First open up your Main.storyboard and make sure that ViewControllerOne (the controller you want to segue from) is embedded in a Navigation Controller.
If it's not, you can do that by clicking on your View Controller, then click on Editor in the top menu bar, go to Embed In and select Navigation Controller.
Then you can create a segue using the Interface Builder by doing control + click on the little yellow icon at the top of ViewControllerOne (not the Navigation Controller) & drag to ViewControllerTwo in the Interface Builder.
Now click on the segue that just got created, and type an identifier of your choice in the Attributes Inspector.
Then in your ViewControllerOne class, you can just perform the segue using the #IBOutlet as you mentioned :
#IBAction func onFirst(_ sender: UIButton) {
performSegue(withIdentifier: "your_segue_identifier", sender: self)
}
Just make sure that the segue identifiers match, and everything should be fine :)
You need redefine target and action for your current navigation item
self.navigationItem.leftBarButtonItem?.target = self
self.navigationItem.leftBarButtonItem?.action = #selector(self.onFirst(_:))

Show Segue uses the wrong presentingViewController, and causes wrong navigation

I am trying to fix up an edit segue (show in IB) where I can click the 'Edit report details' button on the toolbar and it will Show Segue towards the 'Configure Report' View controller.
However, if I click cancel, it goes all the way back to my login screen, because presentingViewController is a UINavigationController, even though it shouldn't be.
Here's the story board. http://i.imgur.com/DK4HhpO.png
Any ideas?
// MARK: Navigation
#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.
let isPresentingInAddItemMode = presentingViewController is UINavigationController
if isPresentingInAddItemMode {
dismissViewControllerAnimated(true, completion: nil)
}
else {
// In this mode (push presentation), we need to pop the view controller to get rid of it, rather than dismissing
navigationController!.popViewControllerAnimated(true)
}
}
This is all I do in my code and it works.
#IBAction func cancel(sender: AnyObject) {
self.navigationController?.popViewControllerAnimated(true)
}
How ever you may need to check for which segue identifier sent you first.
#IBAction func cancel(sender: AnyObject) {
if (segue.identifier == "Edit") {
self.navigationController?.popViewControllerAnimated(true)
} else if (segue.identifier == "Add") {
self.navigationController?.popViewControllerAnimated(true)
}
This way it knows which one to follow. It also may depend on how you are segue to the view in the first place.

Resources