Set segue identifier programmatically - ios

Lets say i have a ViewController with a textField and a button.
I want to use an unwind segue so i could get the information of the textField to my other viewController after i clicked the button.
I want to use the PrepareForSegue method so i could save the text from the textField in a property before i"m "unwiding".
How do i set an identifier to my segue manually? If it was a bar Button item i could use the IB to set the identifier ( to "Save" for example) and then use it. This is not the case, just a regular button.

It is not possible to create segues programmatically. They cannot exist without storyboards.
See this question.

You can easily add a segue programmatically (without using storyboards). In the source view controller header file:
#property (nonatomic, strong) UIStoryboardSegue *segue;
In the source view controller implementation file, set up the segue property accordingly:
self.segue = [UIStoryboardSegue segueWithIdentifier:[NSString stringWithFormat:#"%lu", indexPath.item] source:self destination:[PlayerViewController sharedPlayerViewController] performHandler:^{
// Insert whatever; nothing is needed for a basic segue
}];
Also, in the source view controller, add the destination view controller set up and transition in the prepareForSegue method:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// The code in this method both sets up the destination view controller and transitions to it; you could optionally do set up here only, and then transition in an overridden -(void)perform method. This way is more convenient.
[(PlayerViewController *)segue.destinationViewController setView:[PlayerView sharedPlayerView]];
PHAsset *phAsset = (PHAsset *)AppDelegate.assetsFetchResults[segue.identifier.integerValue];
[AppDelegate.cacheManager requestAVAssetForVideo:phAsset options:AppDelegate.videoOptions resultHandler:^(AVAsset * _Nullable asset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) {
[[PlayerView sharedPlayerView] addSubview:[PlayerControls sharedPlayerControls]];
[[PlayerControls sharedPlayerControls] setDelegate:(PlayerViewController *)segue.destinationViewController];
[[PlayerView sharedPlayerView] addSubview:[AppDelegate playerControlsLabel]];
[(PlayerViewController *)segue.destinationViewController setupPlaybackForAsset:asset completion:^{
[((AssetsCollectionViewController *)sender).navigationController presentViewController:(PlayerViewController *)segue.destinationViewController animated:TRUE completion:^{
}];
}];
}];
}
Also, add this line anywhere in the source view controller implementation file to perform the segue:
[self prepareForSegue:self.segue sender:self];
The call to the prepareForSegue method can be made inside an IBAction handler and anywhere else.
Note that the code that presents the destination view controller references a view controller that is not set up in a storyboard; the project from which this code was taken does not use storyboards for anything. BUT THAT DOESN'T MATTER because the segue code will be the same.
Also note that you do not have to modify the destination view controller in any way, shape or form. To programmatically create an unwind segue (in other words, build on the one-way segue shown here):
#implementation RootNavigationController
- (UIStoryboardSegue*)segueForUnwindingToViewController:(UIViewController *)toViewController fromViewController:(UIViewController *)fromViewController identifier:(NSString *)identifier {
return [toViewController segueForUnwindingToViewController:toViewController fromViewController:fromViewController identifier:identifier];
}
#end
Now, that's what I've done in my own app; here's what someone else has done to create a segue without using the storyboard (vs. not having a storyboard at all):
- (void)presentSignupViewController {
// Storyboard ID
UIStoryboard *modalStoryboard = [UIStoryboard storyboardWithName:#"MyStoryboard" bundle:nil];
UINavigationController *navController = [modalStoryboard instantiateViewControllerWithIdentifier:#"MySignupViewController"];
MySignupViewController *controller = [navController viewControllers][0];
// Configure your custom view controller, e.g. setting delegate
controller.delegate = self;
// Show VC
navController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
BlurryModalSegue *segue = [[BlurryModalSegue alloc] initWithIdentifier:#"SignupScene" source:self destination:navController];
[segue perform];
}
The difference is only in the use of perform and prepareForSegue: when using no storyboard at all, you have to call your transition method of choice (push... or present...) in the performForSegue method; however, the transition method is called for you by the perform method otherwise.
<iframe width="853" height="480" src="https://www.youtube.com/embed/y8Fu2PEO2zo?rel=0&showinfo=0" frameborder="0" allowfullscreen></iframe>

Related

Method not working when called from another class

I am trying to call the method addObjectToArray from SecondViewController.m. The NSLog works, however I cannot add "foo" to _myArray (an NSMutableArray that is the data source for the UITableView). If I call [self addObjectToArray] in viewDidLoad, then it works fine.
FirstViewController.m
-(void)addObjectToArray {
[_myArray addObject:#"foo"];
[_myTableView reloadData];
NSLog(#"it works");
}
SecondViewController.m
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"identifier"]) {
FirstViewController *controller = [segue destinationViewController];
[controller addObjectToArray];
}
}
As per Nofel Mahmood's comment, you are creating two separate instances of FirstViewController, one named firstViewController (created from the storyboard) and one called myObject (created with the 'new' method). You then call the addObjectToArray method on myObject, but you present firstViewController. Your myObject is essentially redundant. Amend your code as follows:
-(IBAction) doSomething:(id)sender {
FirstViewController *firstViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"firstViewController"];
[firstViewController addObjectToArray];
[self presentViewController:firstViewController animated:YES completion:nil];
}
EDIT
Since you want to return to an existing instance of FirstViewController, you need to use an unwind segue. There's a detailed explanation here: what-are-unwind-segues, but in your case:
In FirstViewController, add a new method:
- (IBAction)unwindToFirst:(UIStoryboardSegue *)unwindSegue {
[self addObjectToArray];
}
Then in your storyboard, ctrl-drag from the SecondViewController (or if you prefer, from a specific control in the view) to the green "Exit" icon in the bar below the SecondViewController. You should then select the unwindToFirst action in the small popup that appears.
If you want to use this unwind segue from code, look in the Document Outline on the left hand side of your storyboard for the Unwind Segue you just created. Select this, and then add an identifier in the attributes inspector on the right hand side. You can then call this segue from code using the normal [self performSequeWithIdentifier:...] method.

Button present a navigationViewController with a different embed viewContoller in the container

I have 3 different buttons which upon touch present the same UINavigationViewController with a container.
However, each button represents which view controller will be embed at the container.
How can I embed the necessary viewController by code?
what you can do is use an identifier which would be assigned to your various viewController as storyboardID
such as fisrtVC, SecondVC, thirdVC
the depending upon whichButton is pressed just set the identifier and use this identifier when
you want to push the controller such as
for example
while you push the navigation viewController just pass the storyboard Identifier such as
Declare a NSString *identifier;
-(IBAction)firstButtonClicked{
identifier=#"firstVC";
//pass this identifier to your navigationController
}
similarly for other Controllers
When you push the navigation controller make sure to pass this identifier along now depending upon the value you can initiate the controller on you VC as
on ViewDIdApppear:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
NSString* viewType = passedIdentifier
UIViewCOntroller* viewController = [storyboard instantiateViewControllerWithIdentifier:viewType];
Load this "viewCOntroller in your ContainerView"
You should implement prepareForSegue method:
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
id is your button that will fire segue. You do segues by drugging and drop in storyboard. Put an if statement in this method and tell your UIViewController which UIView to load in container. You can pass data like this:
// Get reference to the destination view controller
YourViewController *vc = [segue destinationViewController];
// Pass any objects to the view controller here, like...
[vc setMyObjectHere:object];
Second snippet taken from this answer.
Update.
To load different UIView in container put if statement into viewWillApper method.
This method firing earlier than viewDidLoad. If statement must check some property that tell what UIView to init. You setting up this property in prepareForSegue.
It will look like this:
if (self.viewToLoad == 1)
{
self.dynamicView = MyCustonUIViewNumberOne *view = [MyCustonUIViewNumberOne alloc] init];
}
Update 2.
Or you can do it dynamically like in this answer:
if (self.viewToLoad == 1)
{
// Replacing with your dimensions
CGRect frame = CGRectMake(x, y, width, height);
MyCustonUIViewNumberOne *dynamicView = [[MyCustonUIViewNumberOne alloc] initWithFrame:frame];
[self.container addSubview: dynamicView];
} else {
// Init other view
}
The container property:

Custom Delegate Callback

I'm new to iOS programming and I'm facing a problem
I'm having a problem with custom delegate.
I'm trying to make a simple custom where it return data to the previous view controller and pop the current view controller.
I have 2 navigation view controller
1 - main view controller
2 - Adding
and here is the protocol that is written in the adding view controller
#protocol AddingDelegate <NSObject>
#required
-(void)setInformation:(Adding *)controller withObject:(Conference *)info;
and here is the where I called it in adding view controller
-(IBAction)addingConference
{
NSLog(#"Adding Button Pressed");
conferenceObject = [[Conference alloc]init];
conferenceObject.name = [NameTX text];
conferenceObject.city = [CityTX text];
conferenceObject.description = [Dectription text];
NSMutableArray *info = [[NSMutableArray alloc] init];
[info addObject:conferenceObject];
[self.delegate setInformation:self withObject:conferenceObject];
NSLog(#"adding Conference method is done");
}
I wrote the delegate at the interface in the main view controller
#interface MainViewController : UITableViewController <AddingDelegate>
#end
and here where I declared the delegate method
-(void)setInformation:(Adding *)controller withArray:(NSMutableArray *)info
{
NSLog(#"in the main view at the delegate");
[self.navigationController popToRootViewControllerAnimated:YES];
NSLog(#"Should be popped right now");
}
and this is the prepare for segue method
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"AddObject"]) {
UINavigationController *navigation = segue.destinationViewController;
Adding *addingViewController = [[navigation viewControllers]objectAtIndex:0];
addingViewController.delegate = self;
}
}
now the problem is when I push the adding on top of the stack and then fill the information and press done the adding view controller doesn't pop to show main view controller.
I tried to log everything and the logs from the main view controller doesn't show .
Please help me
What I notice here is that in the implementation of prepareForSegue:sender: the segue's destinationViewController is a navigation controller. This makes me think that your segue is not pushing the AddingController on the current navigation stack but it's presenting a new one instead. This means the new navigation controller containing the AddingController is presented modally and as such, when you try to pop the navigation stack nothing seems to happen because you're operating on the wrong navigation stack. If that is the case you have two options: 1. change [self.navigationController popToRootViewControllerAnimated:YES]; for [self dismissViewControllerAnimated:YES completion:nil]; or 2. change the segue to be a push segue instead of a modal segue and point the segue directly to the AddingController.
In Adding.m
#class Adding;
#protocol AddingDelegate
- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller;
#end
#interface Adding : UIViewController
#property (weak, nonatomic) id <AddingDelegate> delegate; // have you forgot this one
#end
and use
[self dismissViewControllerAnimated:YES completion:nil];
You need dismiss if you want get back to previous screen and make sure you have added Navigation controller

Segue to ABPersonViewController in Storyboard

I am trying to segue to a ABPersonViewController from a button in the Storyboard.
But if I do that the screen is completely black.
If I use a IBAction for the button and use the following code it works:
ABPersonViewController *person = [[ABPersonViewController alloc]init];
[self.navigationController pushViewController:person animated:YES];
Why is that? am I doing wrong?
EDIT: I found a work around but I don't think this is a proper way to do it. I subclassed ABPersonViewController and overrode the initWithCoder method with the following:
-(id)initWithCoder:(NSCoder *)aDecoder{
self = [self initWithNibName:nil bundle:nil];
return self;
}
Since you are using storyboards you should should name the segue you are using and then this in your IBAction:
[self performSegueWithIdentifier:#"abPersonViewControllerSegue" sender:self];
This way you do not even need to manually call alloc/init. Then in your prepareForSegue you could set any attributes:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"abPersonViewControllerSegue"])
{
[segue.destinationViewController setAttribute:#"whatever you want"];
...
}
}
If this is not what you are looking for please let me know.
You don't set the displayedPerson property ...
#property(nonatomic, readwrite) ABRecordRef displayedPerson
Since you are using Storyboard, why do you still call [[ABPersonViewController alloc]init]?The Storyboard will handle the creation and pushing of the ABPersonViewController(if you specified Push action for the segue). As long as you control dragged from the Button to the ABPersonViewController in story board, you do not need to write a single line of code. If you control dragged from the view controller that contains the button, then you should be calling performSegue to trigger the segue(you need to set the identifier for the segue in this case) that adds the ABPersonViewController to the navigation controller.

UITableViewController loses scroll functionality if presented by a popover as model

Any reason why a model tableview (UITableViewController) is not scrolling a table when the keyboard appears ONLY if presented by a popover controller?
I basically have a popover menu with a button that opens a model Tableview fullscreen, text fields within rows at the bottom of the table get hidden by the keyboard when tapped.
This is not the same as a UIView or Scrollview where you have to manage scrolling manually. The UITableViewController has this functionality built in, it just doesn't work if there is a popover controller somewhere back down the hierarchy.
Sample project: www.geoffcoope.co.uk/ios/testTVCScrolling.zip
Thanks
Geoff
Either your UITableView is sending its delegate method requests to the popover's view controller instead of your UITableView controller, or your UITableView's delegate is set to nil. Look where you are instantiating your TableView and make sure you are setting the delegate property correctly.
I guess your problem is with a way a storyboard's segue shows popover controllers. It looks like it is a common problem.
So, the easiest way to solve it, is to move the creation of your modal window inside a code. Something like that:
Inside your ViewController.h write
#property (strong) UIPopoverController * popoverController;
- (void) showNavigationController;
Inside your ViewController.m write
#synthesize popoverController;
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
[super prepareForSegue:segue sender:sender];
if ([segue isKindOfClass:[UIStoryboardPopoverSegue class]]){
if (popoverController) [popoverController dismissPopoverAnimated:NO];
UIStoryboardPopoverSegue * popoverSegue = (UIStoryboardPopoverSegue *) segue;
popoverController = popoverSegue.popoverController;
}
}
- (void) showNavigationController
{
[popoverController dismissPopoverAnimated:NO];
popoverController = nil;
UIStoryboard *ub = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
UIViewController *controller = [ub instantiateViewControllerWithIdentifier:#"navigationController"];
[self presentModalViewController:controller animated:YES];
}
then inside your MainStoryboard set identifier navigationController for your navigation controller and instead of segue on your "Show Table" button set a "touch up inside" delegate. This delegate will execute your showNavigationController method from ViewController (using a NSNotificationCenter, as example)

Resources