Performwithsegue and programatically switching views - Objective-C - objective-c

I am having a problem, I want to change to another viewcontroller when a timer expires and this works with this code:
- (IBAction)Akkoord:(id)sender {
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *vc = [mainStoryboard instantiateViewControllerWithIdentifier:#"Innameformulier"];
[self presentViewController:vc animated:YES completion:nil];
[self performseguewithidentifier:#"nextcontroller"]
}
But when I use this, my variables in my prepareforsegue method are not passing. How can I do this? I already tried [self performseguewithidentifier] but this is not working.
my preparforsegue code is:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
Innameformulier *IForm = [segue destinationViewController];
IForm.SignatureTransport = _drawImage.image;
IForm.KlantnaamInname = _Klantnaamtransport;
}
How can I call this function to happen on the timer?

In Interface Builder, select the View Controller from which you want to fire a segue, CTRL-drag from it to the destination View Controller and create a segue.
Select the segue, open the Attributes Inspector and create an Identifier for it.
Go back to your View Controller Class that fires the segue and if you don't already have it commented, add the method prepareForSegue:
In your prepareForSegue: method you should have something like the following:
- (void)prepareForSegue:(UIStoryboardSegue *)segue {
if ([segue.identifier isEqualToString:#"XXXX"]) {
DestinationVC *dVC = segue.destinationViewController;
dVC.attribute1 = self.anAttribute;
dVC.attribute2 = self.anotherAttribute;
}
}
And now you can fire the segue by calling [self performSegueWithIdentifier:#"XXXX"].
Note: you can also fire an action segue from an UIControl subclass like UIButton

OK, what you are doing here is bypassing the segue completely. You need to do something like this...
- (IBAction)Akkoord:(id)sender {
[self performseguewithidentifier:#"nextcontroller"]
}
This is all you need to do. The segue will then create the nextController for you and then the code inside prepareForSegue will pass the variables across.
If you want it on a timer then you just need to set up a timer and when it expires you can run...
[self performseguewithidentifier:#"nextcontroller"]
As long as you have a segue with this identifier then it will work anywhere. Ever after a timer.

Related

How do I catch when UIViewController displays after dismissViewControllerAnimated

I've got two UIViewControllers. I'm using modal segue to the second one, when coming back I use dismissViewControllerAnimated. I want to fire a method when I come back to the first one. How can I do that?
I tried to fire a custom notification before dismissViewControllerAnimated and catching it in the first UIViewController, but it doesn't catch anything, because it's still on the second one when it's fired.
There are easy options I can see.
Use the viewDidDisappear: method in the view you're dismissing.
dismissViewControllerAnimated:completion: method accepts a block that actually executes after viewDidDisappear executes in the dismissing view.
To pass a reference from one view controller to the next:
In the second view controller's .h file, add a property:
#property (nonatomic,strong) FirstViewController *firstVC;
In your first view controller, add the following method:
- (void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender {
if([#"segue_YOUR_SEGUE_NAME" isEqualToString:[segue identifier]]) {
if([[segue destinationViewController] isKindOfClass:
[SecondViewController class]]) {
SecondViewController *dest = (SecondViewController*)[segue
destinationViewController];
dest.firstVC = self;
}
}
}
Now, in your second view controller, you can do two things, as I already stated:
[self dismissViewControllerAnimated:YES
completion:^{
[self.firstVC someMethod];
}];
OR...
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[self.firstVC someMethod];
}

Storyboard UIViewController not appear

I recently started using storyboard.
I have the main viewcontroller, which by code must call another viewcontroller.
in storyboard, I created a "segue" that goes from the first to the second controller, with the identifier "segueToSignSelection."
in the main viewcontroller, when I need to call up the second viewcontroller, insert this code:
[self performSegueWithIdentifier:#"segueToSignSelection" sender:self];
that calls this method:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([[segue identifier] isEqualToString:#"segueToSignSelection"])
{
signSelectionViewController = (SignSelectionViewController*)[segue destinationViewController];
[self presentModalViewController:segue.destinationViewController animated:YES];
[segue.destinationViewController test];
}
}
in the second controller, the test method is invoked. But the view does not appear. What could be the problem?
Thanks

Call storyboard scene programmatically (without needing segue)?

I have a modal storyboard scene that I want to be accessible to all my other scenes. Creating a modal segue to it from every scene on my storyboard creates a big mess of strings going everywhere. Is there a way that I leave off the segues and call the scene programmatically instead?
Basically I want to do something like this:
MyNewViewController *myNewVC = [[MyNewViewController alloc] init];
[self presentModalViewController:myNewVC animated:YES];
except instead of creating and pushing a view controller class, I want to do a modal transition to an "isolated" (not connected with a segue) storyboard scene.
Yes you can. Do something like this to get access to the VC, then just Modal Push it:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:nil];
MyNewViewController *myVC = (MyNewViewController *)[storyboard instantiateViewControllerWithIdentifier:#"myViewCont"];
Note: the method presentModalViewController:animated is deprecated in iOS 6.
The new code should read:
NSString * storyboardName = #"MainStoryboard_iPhone";
NSString * viewControllerID = #"ViewID";
UIStoryboard * storyboard = [UIStoryboard storyboardWithName:storyboardName bundle:nil];
MyViewController * controller = (MyViewController *)[storyboard instantiateViewControllerWithIdentifier:viewControllerID];
[self presentViewController:controller animated:YES completion:nil];
In the storyboard give your view controller an identifier (under the Attributes Inspector) then use the following code to bring that view forward.
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"STORYBOARDNAME" bundle:nil];
UIViewController *vc = [mainStoryboard instantiateViewControllerWithIdentifier:#"VIEWCONTROLLERIDENTIFIER"];
[self presentModalViewController:vc animated:YES];
I have a case where I want to present a view controller from the main part of the app, one with settings & help & so on. To do this, I want it to be within a nav controller, sort of a little plug in module we can call from a UIBarButtonItem.
Now, this can be to/in the current storyboard, or to another, it doesn't matter.
I want to do it this way, because I loathe the potential of segue line spaghetti all over my storyboard.
Here's how to do it.
- (IBAction)displaySettings:(id)sender
{
LOG_SELECTOR() // google that for extra goodness
// FYI, this can be done using a different storyboard like so.
/*
NSString * storyboardName = #"MainStoryboard_iPhone"; // possibly use device idiom?
UIStoryboard * storyboard = [UIStoryboard storyboardWithName:storyboardName bundle:nil];
*/
// To push a new set of scenes with a new Navigation Controller, it is done like this:
UINavigationController *settingsNC = [self.storyboard instantiateViewControllerWithIdentifier:#"Settings Nav Controller"];
OBSettingsUIViewController *settingsVC = [self.storyboard instantiateViewControllerWithIdentifier:#"Settings root"];
[settingsNC pushViewController:settingsVC animated:NO];
[settingsNC setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
// Present the view controller;
[self presentViewController:settingsNC animated:YES completion:NULL];
}
In the presented view controllers (or in a subclass of the Navigation Controller), you can have a UIBarButtonItem to then dismiss the whole presented hierarchy of view controllers like so:
- (IBAction)dismissThisVC:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
Hope this helps a bunch of people out. Cheers.
Just call viewcontroller using navigation controller
Write this code in viewcontroller and set viewcontroller in storyboard as set in the image.
ProfileVC *vc = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"ProfileVC"];
[self.navigationController pushViewController:vc animated:YES];
Call to navigate to other class
UIWindow *window = [[[UIApplication sharedApplication] windows] objectAtIndex:0];
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle: nil];
UINavigationController *navController = (UINavigationController *)window.rootViewController;
DumpFeed *dump = [storyboard instantiateViewControllerWithIdentifier:#"DumpFeed"];
dump.isPushed=YES;
dump.strUserId = appDelegate.strFriendid;
[navController pushViewController:dump animated:YES];
Heres a Swift version of this:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let myVC = storyboard.instantiateViewControllerWithIdentifier("myStoryId")
self.presentViewController(myVC, animated: true, completion: nil)
You should also change your storyboard id like this:
I think that with iOS7 it has become very easy implementing via the storyboard
I'm currently learning about the new features in iOS7 and found this simple solution, but it might have been relevant even in prior versions, I'm not sure.
First u need to connect the presenting VC with the target VC (thats the only connection needed), then within the storyboard's attributes inspector choose the style to be modal, in the identity inspector give your VC a storyboardID and make sure you checked the 'use storyboardID' checkbox,
If its not there yet add this method to your presentingVC:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
YourTargetVC * targetVC =
(YourTargetVC *)segue.destinationViewController;
if(nil != targetVC) {
//Do preparations here
}
}
Now, when you wish to show your targetVC from your presentingVC you can use:
[self performSegueWithIdentifier:(NSString *) sender:(id)];
where the identifier is your viewController's storyboardID, and the sender is the view who triggered the action, this method will invoke the storyboards scene, so the [prepareForSegue: sender:] method will be called allowing u making last modifications before the targetViewController will appear.

It is possible to use an existing ViewController with PerformSegueWithIdentifier?

I use the method performSegueWithIdentifier:sender: to open a new ViewController from a storyboard-file programmatically. This works like a charm.
But on every time when this method is being called, a new ViewController would be created. Is it possible to use the existing ViewController, if it exista? I don't find anything about this issue (apple-doc, Stack Overflow, ...).
The Problem is:
On the created ViewController the user set some form-Elements and if the ViewController would be called again, the form-elements has the initial settings :(
Any help would be appreciated.
Edit:
I appreciate the many responses. Meanwhile, I'm not familiar with the project and can not check your answers.
Use shouldPerforSegueWithIdentifier to either allow the segue to perform or to cancel the segue and manually add your ViewController. Retain a pointer in the prepareForSegue.
... header
#property (strong, nonatomic) MyViewController *myVC;
... implementation
-(BOOL) shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender{
if([identifier isEqualToString:#"MySegueIdentifier"]){
if(self.myVC){
// push on the viewController
[self.navigationController pushViewController:self.myVC animated:YES];
// cancel segue
return NO;
}
}
// allow the segue to perform
return YES;
}
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"MySegueIdentifier"]){
// this will only be called the first time the segue fires for this identifier
// retian a pointer to the view controller
self.myVC = segue.destinationViewController;
}
}
To reuse an existing UIViewController instance with a segue create the segue from scratch and provide your own (existing) destination (UIViewController). Do not forget to call prepareForSegue: if needed.
For example:
UIStoryboardSegue* aSegue = [[UIStoryboardSegue alloc] initWithIdentifier:#"yourSegueIdentifier" source:self destination:self.existingViewController]
[self prepareForSegue:aSegue sender:self];
[aSegue perform];
Following code makes singleton view controller.
Add them to your destination view controller implementation, then segue will reuse the same vc.
static id s_singleton = nil;
+ (id) alloc {
if(s_singleton != nil)
return s_singleton;
return [super alloc];
}
- (id) initWithCoder:(NSCoder *)aDecoder {
if(s_singleton != nil)
return s_singleton;
self = [super initWithCoder:aDecoder];
if(self) {
s_singleton = self;
}
return self;
}
I faced this problem today and what I have done is to create the view controller manually and store it's reference.
Then every time I need the controller, check first if exists.
Something like this:
MyController *controller = [storedControllers valueForKey:#"controllerName"];
if (!controller)
{
controller = [[UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:NULL] instantiateViewControllerWithIdentifier:#"MyControllerIdentifierOnTheStoryboard"];
[storedControllers setValue:controller forKey:#"controllerName"];
}
[self.navigationController pushViewController:controller animated:YES];
Hope it helps.
Create a property for the controller.
#property (nonatomic, weak) MyController controller;
And use some kind of lazy initialization in performSegueWithIdentifier:sender
if (self.controller == nil)
{
self.controller = [MyController alloc] init]
...
}
In this case, if controller was already created, it will be reused.
Firstly you would be going against Apple's design in Using Segues: "A segue always presents a new view controller".
To understand why it might help to know that what a segue does is create a new view controller and then the perform calls either showViewController or showDetailViewController depending on what kind of segue it is. So if you have an existing view controller just call those methods! e.g.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
Event *object = [self.fetchedResultsController objectAtIndexPath:indexPath];
self.detailViewController.detailItem = object;
[self showDetailViewController:self.detailViewController.navigationController sender:self];
}
You would need to make the Viewcontroller into a singleton class.

How to set the delegate with a storyboard

I've been debating with this for a while now, hope you can help me.
I've been creating an app using storyboards mostly, I have a point where I popup a modal box to add a new record, popup works fine, the problem is dismissing it.
I've followed Apple's instructions on how to properly close modal boxes using delegates, and that works fine, except I need to add a navigation controller to my modal box, because the add process requires two steps (here fullscreen):
The problem lies in setting the delegate, so here are my two questions:
1- In my root view class (My Tab) is a delegate of the Add class (the modal), everything is set up right except this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showAdd"]) {
[[segue destinationViewController] setDelegate:self];
}
}
The problem lies in that [segue destinationViewController] is returning the navigationcontroller and not the AddDrinkViewController class (see the storyboard). How do I get around this? If I remove the navigation controller altogether, the code works fine setting the appropriate delegate.
2- Is there any way to set the delegate by dragging the outlets in the storyboard?
Thanks!
You're right, the destinationViewController will be a UINavigationController in this case. I wrote a category to handle this common situation:
// category .h file
#interface UIStoryboardSegue (NavControllerExtensions)
// Gets destinationViewCotroller. But if that controller
// is a NavigationController, returns the nav controller's
// top level view controller instead.
#property (readonly) id topLevelDestinationViewController;
#end
// category .m file
#implementation UIStoryboardSegue (NavControllerExtensions)
- (id)topLevelDestinationViewController
{
id dest = self.destinationViewController;
if ([dest isKindOfClass:[UINavigationController class]]) {
UINavigationController* nav = dest;
dest = nav.topViewController;
}
return dest;
}
#end
So now you can just do this in any of your prepareForSegue methods, and not need to worry about whether there even exists a NavigationController:
[[segue topLevelDestinationViewController] setDelegate:self]
// another example:
MyViewController *vc = segue.topLevelDestinationViewController;
vc.delegate = self; // etc.
To answer your second question, I couldn't find a way to set the delegate within IB.
I found a shorter way in my case (same as yours):
AddDrinkViewController *controller=[[[segue destinationViewController]viewControllers]objectAtIndex:0];
Basically you need to create an
Instance of UINavigationController and assign destinationViewController to it
and grab its topView controller
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showAdd"]) {
UINavigationController *navigationController = segue.destinationViewController;
AddDrinkViewController *addDrinkcontroller = (AddDrinkViewController *)navigationController.topViewController;
addDrinkcontroller.delegate = self;
}
}