People, i want to pop a viewController using the normal back button on NavigationView controller without release the customized view that was created by the user, any one know some way to do that?
Because the natural flow of the navigation controller is release the "poped" viewController! Thanks for the help!
You need to retain a copy of the view controller elsewhere. Perhaps inside the class containing the navigation controller. Then push this back onto the stack when required.
Additionally check out UINavigationControllerDelegate
can you save the datas in the "popped" view controller? when it comes out again, populate it? When view controller is popped, it should be released.
You can navigation.viewControllers array copy in to global array before pop the custom view. After pop view global navigation.viewControllers assign global array.
NSArray create in AppDelegate
appDelegate.nav = self.navigationController.viewControllers;
[self.navigationController popViewControllerAnimated:YES];
then after assign global array in poped view
-(void)viewWillAppear:(BOOL)animated
{
self.navigationController.viewControllers = appDelegate.nav;
}
Well people tha answer is, make your controller from the StoryBoard, and don't use segue to call.
if(comparacao == nil)
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main_iPhone" bundle: nil];
comparacao = [storyboard instantiateViewControllerWithIdentifier:#"ComparacaoView"];
}
[self.navigationController pushViewController:comparacao animated:YES];
So with this, I every use the before instance created, and every thing that my user do in this view was maintained.
Swift Code:
#IBAction func pushButtonAction(_ sender: UIButton) {
if let exitingSecViewCon = secondViewController {
navigationController?.pushViewController(exitingSecViewCon, animated: true)
}else{
self.performSegue(withIdentifier: "second", sender: self)
}
}
var secondViewController: SecondViewController? //Keep Strong Pointer
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "second" {
if let sec = segue.destination as? SecondViewController {
sec.name = "ABC"
secondViewController = sec;
}
}
}
Console Log:(Same address for SecondViewController)
ADD=<RetainCount.SecondViewController: 0x7fb5ac630cf0>
ADD=<RetainCount.SecondViewController: 0x7fb5ac630cf0>
ADD=<RetainCount.SecondViewController: 0x7fb5ac630cf0>
Related
I want to pass data from ViewController1 to ViewController2. ViewController2 is embedded within a Navigation Controller (because I have a Segmented Control that corresponds to 3 Child View Controllers). My segue is directly from ViewController1 to the Navigation Controller.
I tried the following in ViewController1:
ViewController2().testID = "test value"
With the following in ViewController2:
var testID = ""
However testID does not update when I run print(testID) in ViewController2.
What is recommended for passing data from ViewController1 to ViewController2? Any help/advice is much appreciated!
You can pass the value by overriding the prepare(for segue:) function in ViewController1
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination
guard let destination = segue.destination as? UINavigationController else {
return
}
guard let finalDestination = destination.viewControllers.first as? ViewController2 else {
return
}
finalDestination.testID = "Test Value"
}
NSUserDefaults will always work.
Hello,
so i have this storyboard structure i have a button in ViewController B i need that button to point to the bottom NavigationController in which it has the ViewController C as rootViewController so when i press a button in ViewContoller B it takes me to C now i need a back button on C to take me back to B
i have tried looking but all of the reference materials show how to navigate between ViewController not NavigationControllers
I think you cannot push Navigation Controller inside another uinavigation controller but alternativaly you can present another nav controller.
Hope it works for you.
I have created simple demo like this.
class ViewController: UIViewController {
#IBOutlet weak var btnCustom: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func btnForward(_ sender: Any) {
/*Uncomment the code if you want to present programatically*/
/*if let navi2 = self.storyboard?.instantiateViewController(withIdentifier: "nav2"){
self.present(navi2, animated: true) {
}
}*/
}
}
class ViewController2: UIViewController {
#IBOutlet weak var btnCustom: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func btnBack(_ sender: Any) {
if let parentNav = self.navigationController {
parentNav.dismiss(animated: true) {
}
}
}}
You can use a single navigation controller, but overwrite the back buttons action.
By default it will pop the current view controller from the navigation stack and go to the previous one.
On the back action in VC B just push to the VC C, you can do this either in storyBoard by drag-drop from the back button to VC C or by code by writing:
self.navigationControlller.push(VC C)
If using 2 navigationControllers is a requirement that cannot be avoided, you can solve the problem statement in the following way.
In ViewControllerC on a UIButton's tap event, get the instance of ViewControllerB in the parent navigationController and pop to it like so,
#objc func backAction() {
let parentNav = self.navigationController?.navigationController
if let vcB = parentNav?.viewControllers.first(where: { $0 is ViewControllerB }) {
parentNav?.popToViewController(vcB, animated: true)
}
}
Add the backAction() to backButton's target like,
backButton.addTarget(self, action: #selector(backAction), for: .touchUpInside)
Put this code inside your C's viewController back button action method. Also you have to give identifiers names to your views in the storyboard screen.
...
//It possible to change between different storyboards, but in your case all the views share the same storyboard.
UIStoryboard* storyboardOfA_B = [UIStoryboard storyboardWithName: #"storyboardOfA_B" bundle:nil];
UINavigationController* navigationControllerOfA_B = [storyboardOfA_B instantiateViewControllerWithIdentifier:#"navigationControllerOfA_B"];
UIViewController* viewControllerB = [storyboardOfA_B instantiateViewControllerWithIdentifier:#"viewControllerB"];
//I'm supposing that you have the action method of the back button in the C's view controller so I use self.
UINavigationController* navigationControllerOfC = self.navigationController;
[navigationControllerOfC pushViewController: navigationControllerOfA_B animated:NO];
[navigationControllerOfA_B pushViewController: viewControllerB animated:NO];
...
Maybe this is not the best way but is a way
I have three View Controllers embedded in Navigation Controller. I want to go from VC1 to VC3 so that in VC3 the Navigation Item's back button would direct the user to VC2 instead of VC1. I think this should be done either by adding the VC2 to the Navigation Stack between VC1 and VC3 when VC3 is created or by skipping the second View Controller.
I tried this:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let identifier = segue.identifier {
switch identifier {
case "JumpToThirdVCSegue":
if let secondvc = segue.destinationViewController as? SecondViewController {
secondvc.performSegueWithIdentifier("ThirdVCSegue", sender: self)
}
default: break
}
}
}
but I couldn't get it working. Perhaps performing a segue isn't possible when the View Controller isn't open yet?
What is the best way to skip a View Controller / add a View Controller in the middle of Navigation Stack? I hope this helps you to understand what I'm trying to do:
Something like this should work:
self.navigationController?.pushViewController(viewController2, animated: true)
self.navigationController?.pushViewController(viewController3, animated: true)
EDIT:
If you want to push the second view controller without being noticed by the user you need to add it to the navigation controller after the third view controller is pushed. This can be done by implementing UINavigationControllerDelegate. You can store your second view controller in a variable and insert it to the navigation controllers hierarchy in delegate method. Your main view controller will look like following:
class MyViewController: UIViewController, UINavigationControllerDelegate {
var viewControllerToInsertBelow : UIViewController?
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.delegate = self
}
func pushTwoViewControllers() {
if let viewController2 = self.storyboard?.instantiateViewControllerWithIdentifier("id1"),
let viewController3 = self.storyboard?.instantiateViewControllerWithIdentifier("id2") { //change this to your identifiers
self.viewControllerToInsertBelow = viewController2
self.navigationController?.pushViewController(viewController3, animated: true)
}
}
//MARK: - UINavigationControllerDelegate
func navigationController(navigationController: UINavigationController, didShowViewController viewController: UIViewController, animated: Bool) {
if let vc = viewControllerToInsertBelow {
viewControllerToInsertBelow = nil
let index = navigationController.viewControllers.indexOf(viewController)!
navigationController.viewControllers.insert(vc, atIndex: index)
}
}
}
if var navstack = navigationController?.viewControllers{
navstack.append(contentsOf: [vc1,vc2])
navigationController?.setViewControllers(navstack, animated: true)
}
Works well
Edit: using swift and segues, this should work:
override func performSegueWithIdentifier(identifier: String?, sender: AnyObject?) {
super.performSegueWithIdentifier(identifier, sender: sender);
if identifier == "JumpToThirdVCSegue" {
// now the vc3 was already pushed on the navigationStack
var navStackArray : [AnyObject]! = self.navigationController!.viewControllers
// insert vc2 at second last position
navStackArray.insert(viewController2, atIndex: navStackArray.count - 2)
// update navigationController viewControllers
self.navigationController!.setViewControllers(navStackArray, animated:false)
}
}
So you override the performSegueWithIdentifier in the VC1 in order to change the navigation stack when the segue to VC3 has actually been performed (and is not only in preparation).
This assumes, that you want to handle this special navigation logic in VC1.
You can use method
func setViewControllers(_ viewControllers: [UIViewController],
animated: Bool)
Here's the documentation (link) that solves the UIViewController skipping problem.
Simply pass all the needed UIViewControllers to it (in navigational order) and the last one will be made as the current active UIViewController (if it already isn't the active one).
My solution would be to keep a BOOL property of when you should skip to third and when not skip, like declaring ’shouldSkip’ in VC2 so that if u set it in prepare segue like below you can act according to that in VC2
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let identifier = segue.identifier {
switch identifier {
case "JumpToThirdVCSegue":
secondvc.shouldSkip=true
}
default: break
}
}
}
and then in viewDidload VC2 you should check this BOOL and if it is true and perform segue and if not just move on
you could also pass pass whenever that kind of skip is not necessary
Building off of MarkHim's answer, I was getting a Black screen when navigating back to the inserted view controller so I came up with the following solution.
BTW - I wanted to insert the new view controller directly under the view controller I just segued to hence the navStack.count - 1 instead of navStack - 2.
Here is my solution:
override func performSegue(withIdentifier identifier: String, sender: Any?) {
super.performSegue(withIdentifier: identifier, sender: sender)
if identifier == "JumpToThirdViewControllerSegue",
let navController = navigationController { // unwrap optional
// initialize the view controller you want to insert
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewControllerToInsert = storyboard.instantiateViewController(
withIdentifier: "SecondVC") as! SecondViewController
// set any passed properties
viewControllerToInsert.passedProperty = propertyToPass
// create an object using the current navigation stack
var navStackArray: [UIViewController]! = navController.viewControllers
// insert the newly initialized view controller into the navStackArray
navStackArray.insert(viewControllerToInsert, at: navStackArray.count - 1)
// replace the current navigation stack with the one you just
// inserted your view controller in to
navController.setViewControllers(navStackArray, animated: false)
}
}
Hi I have 4 view Controllers
MainVC ----> NavBar ----> MyViewController----->UIWebView
Inside my MainVC based on segue, I want to set different data to tableview controller. Here is my prepareforsegue method
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var navController = segue.destinationViewController as UINavigationController
var HVController = navigationController?.viewControllers[0] as MyViewController
HVController.somevar = "Something"
}
However program crashes. How can I pass data from my MainVC to MyViewController? Thanks.
EDIT: I have found my stupid mistake the code should have been as
var HVController = navController.viewControllers[0] as MyViewController
Sorry for wasting your time.
First you have to add a variable in your Target ViewController (MyViewController).
For example:
var passValue:String!
Then, you can pass value from your current ViewController to the target:
if segue.identifier == "yourSegueIdentifier" {
(segue.destinationViewController as MyViewController).passValue = yourObjectToPass
}
Just wondering about the sender object.
I know that you can access it in the method prepareForSegue... but is it available at all in the next destinationViewController?
i.e. in the viewDidLoad could I access a segueSender object or something?
I haven't ever seen this in documentation I just thought it might be useful.
EDIT FOR CLARITY
I'm not asking how to perform a segue or find a segue or anything like this.
Say I have two view controllers (VCA and VCB).
There is a segue from VCA to VCB with the identifier "segue".
In VCA I run...
[self performSegueWithIdentifier:#"segue" sender:#"Hello world"];
My question is can I access the #"Hello world" string from VCB or is it only available in the prepareForSegue... method inside VCA?
i.e. can I access the segue sender object from the destination controller?
Yes you can.
Old question but Ill pop in an answer using swift
first you call
performSegueWithIdentifier("<#your segue identifier #>", sender: <#your object you wish to access in next viewController #>)
in prepareForSegue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let dest = segue.destinationViewController as! <# your destination vc #>
dest.<# sentThroughObject #> = sender
}
}
Then in vc you segue to have a var that will accept the passed through object
class NextViewController: UIViewController {
var <# sentThroughObject #> = AnyObject!()
//cast <# sentThroughObject #> as! <#your object#> to use
}
UPDATE: SWIFT 3
performSegue(withIdentifier: "<#your segue identifier #>", sender: <#Object you wish to send to next VC#>)
override func prepare(for segue: NSStoryboardSegue, sender: Any?) {
let destination = segue.destinationController as! <# cast to your destination VC #>
destination.sentViaSegueObject = sender as? <#your object#>
}
In destination VC have a property to accept the sent Object
var sentViaSegueObject = <#your object#>?
You can always get a segue from the storbyoard, and get the sourceViewController. Something like this:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"myStorboard" bundle:nil];
UIStoryboardSegue *mySegue = [storyboard instantiateViewControllerWithIdentifier:#"mySegue"];
[mySegue sourceViewController];
EDIT:
Comments beat me to it. ;)
If you are trying to pass an object from one view controller to the next you may want to do something like this:
In your parent view controller:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Only if you have multiple segues.
if ([segue.identifier isEqualToString:#"NameOfYourSegue"]) {
// Cast necessary to access properties of the destination view controller.
[(YourChildViewController *)segue.destinationViewController setObject:theObjectToPass];
}
}
You need to give your segue a name in the storyboard and declare a property for the object to pass in the destination view controller.