In UIViewController A I performSegue to UIViewControllerB.And in the prepaerForSegue function of A, I use set function set a strong variable 's value of B. However, when the B's viewDidLoad() called, the value is nil.
Anybody help me and tell me why?
my code is:
UIViewController A:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
CXPWDViewController *cxpwdViewController=[[CXPWDViewController alloc] init];
//I can't use the segue.desinationViewController, cause it is a present modally , the desinationViewController is navigationViewController.
[cxpwdViewController setAction:#"EnterPasscode"];//here the cxpwdViewController .action is #"EnterPasscode"
}
UIViewController B:
-(void) viewDidLoad()
{
[super viewDidLoad];// here the cxpwdViewController .action is nil; Why?
}
If you are presenting an UINavigationViewController, then the rootViewController controller should be your CXPWDViewController. So that you can typeacast the same. Since you are creating an anaother viewcontroller instance you are not getting the value.Try this code.
UIViewController A:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([[segue destinationViewController] isKindOfClass:[UINavigationViewController class]])
{
UINavigationViewController* destinationNavController =(NavigationViewController*)[segue destinationViewController];
CXPWDViewController *cxpwdViewController=(CXPWDViewController*)[destinationNavController.viewControllers firstObject];
if([cxpwdViewController isKindOfClass:[CXPWDViewController class]])
{
[cxpwdViewController setAction:#"EnterPasscode"];
}
}
}
CXPWDViewController *cxpwdViewController=[[CXPWDViewController alloc] init];
//I can't use the segue.desinationViewController
I don't know why you think you can't use the segue destination view controller. But in any case you certainly can't do what you want to do this way. You are creating a completely different CXPWDViewController - and then destroying it an instant later. You won't get far that way!
Related
I have used prepareForSegue successfully, which I have managed to pass variables though. I am trying to now pass a NSNumber through the reverse of the segue but prepareForSegue is not getting called. To get back to my previous VC I am using:
[self.navigationController popViewControllerAnimated:YES];
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"ShowImages"]) {
DiaryViewController *photoNumber = [segue destinationViewController];
photoNumber.deleteObject = self.rowTodelete;
}
Is there something I can add to make the prepareForSegue to work in reverse. Or would I need to access my NSNumber from the different class somehow?
If you set up an unwind segue, you can get the UIViewController that is causing the unwind.
Check out this answer here about Unwind Segues.
Once you actually set up the unwind segue structure, here's some sample unwind segue code:
- (IBAction)unwindToRed:(UIStoryboardSegue *)unwindSegue {
UIViewController* sourceViewController = unwindSegue.sourceViewController;
if ([sourceViewController isKindOfClass:[DiaryViewController class]] {
DiaryViewController *photoNumber = (DiaryViewController *)sourceViewController;
NSNumber *deleteObject = photoNumber.deleteObject;
}
}
You could also use the Delegate-Pattern for it.
Just set the current Controller as the Delegate of the destination controller and create a protocol that defines which method the delegate should be implement.
Then you can pass any parameters you want back to the source Viewcontroller if your destination controller gets dismissed.
// Source Controller (which is also the delegate of the destination-controller)
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
SomeController * a = segue.destinationController;
a.delegate = self;
}
// Delegate Method
-(void)viewControllerGotDismissed:(NSNumber*)test{
NSlog(#"TEST:%#",test);
}
// Destination Controller
#protocol SomeProtocol
-(void)viewControllerGotDismissed:(NSNumber*)test;
#end
// Destination Controller
-(void)viewWillDisappear {
// ....
[self.delegate viewControllerGotDismissed:#1337]; // Pass the value to the source Controller
}
#end
Another method that could work for you is assigning a completion block to run when the presented view is dismissed. For example, if A presents B with a segue, then set a completion block when you initially segue to B. When B is about to be dismissed, run the completion block.
In ViewControllerB.h
#property (nonatomic, strong) void (^onDismiss)(UIViewController *sender, NSNumber *aNumber);
In ViewControllerB.m
// execute the block right before A is presented again
- (void)viewWillDisappear:(BOOL)animated {
self.onDismiss(self, aNumberToPassBack); // Run the block
}
In ViewControllerA.h
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"SegueToB"]) {
ViewControllerB *vcb = [segue destinationViewController];
vcb.onDismiss = ^(UIViewController *sender, NSNumber *aNumber) {
self.aNumber = aNumber;
// ... update your views if needed
};
}
}
I'm trying to pass some data between UIViewControllers in a storyboard. I have some code below to pass the data. Whne I log the string that i'm saving on the original screen it's fine, but when I go to the UIDetailViewController the go is returning (null). What's my issue??
Original View Controller:
-(void)tableView:(UITableView *) tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView==_countryTableView) {
if (indexPath.section==0) {
AthleteDataTransfer *dataTransfer = [[AthleteDataTransfer alloc] init];
dataTransfer.name=#"Yo";
dataTransfer.dob=#"01/01/2000";
dataTransfer.description=#"I am awesome";
dataTransfer.hometown=#"Launy";
NSLog(#"%#", dataTransfer.name);
[self performSegueWithIdentifier:#"showDetail" sender:self];
}
}
}
Detail View Controller:
AthleteDataTransfer *dataTransfer = [[AthleteDataTransfer alloc] init];
_name.text=dataTransfer.name;
dataTransfer.dob=_dob.text;
dataTransfer.description=_description.text;
dataTransfer.hometown=_hometown.text;
NSLog(#"%#", dataTransfer.name);
Insted of passing data in tableViews didSelectRowAtIndexPath, pass the data in prepareForSegue method.
Like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showDetail"]) {
AthleteDataTransfer *dataTransfer = (AthleteDataTransfer *)[segue destinationViewController];
dataTransfer.name=#"Yo";
dataTransfer.dob=#"01/01/2000";
dataTransfer.description=#"I am awesome";
dataTransfer.hometown=#"Launy";
}
}
You should use prepareForSegue method to accomplish this.
and if you have more than one segues from this ViewController you can use segue.Identifier to distinguish
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if(segue.identifier==#"yoursegueidentitifer")
{
DetailedViewController *detailController = [segue destinationViewController];
detailController.nameoftextview.text =self.textViewName.text;
}
}
As suggested in other answers out here you should pass data in the prepareForSegue method. I'am not going to repeat the same code here. I wanted point out one thing that you dont want to create the DetailViewController again in the detailViewController like below
AthleteDataTransfer *dataTransfer = [[AthleteDataTransfer alloc] init]; // This is wrong and not required at all
NSLog(#"%#", dataTransfer.name);
Instead you should use self to refer the current controller
NSLog(#"%#", self.name); // This would be enough
For posting the text data to your destination View Controller, you can use the prepareForSegue method like this.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
DestinationViewController *destController = [segue destinationViewController];
destController._yourTextView.text == _yourText.text;
}
}
Here, DesctinationViewController is the controller to which you want to pass the values and in the next step, you assign the values by using its object and the properties declared on that controller.
Since you are using performSegueWithIdentifier, this will automatically call prepareForSegue after performSegueWithIdentifier is executed which will show up your destination View Controller.
I got a problem with a universal app that were initial design for the iPhone and I used a 3rd party class for a popup on the iPhone. on the iPad I want to use the Apple provided popup. My problem is my code is written so that the View Controller for the popup already is initialized in the viewDidLoad of the class containing the popup, and when seguing - a new instance of that class is allocated. Is there a way to pass it the bckMusicPlayer that´s already been allocated in prepareForSegue?
#property (nonatomic,strong) MJDetailViewController *bckMusicPlayer; //Already initialized when prepareForSegue get´s called. I want to segue to this object.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSLog(#"prepare for segue");
if ([segue.identifier isEqualToString:#"Show iPad Player"]) {
if ([segue isKindOfClass:[MJDetailViewController class]]) {
}
}
}
edit: I got it working passing it the player (AVAudioPlayer) object from bckMusicPlayer
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"Show iPad Player"]) {
NSLog(#"class: %#",[segue class]);
if ([segue.destinationViewController isKindOfClass:[MJDetailViewController class]]) {
NSLog(#"prepare for segue");
MJDetailViewController *destinationVC = segue.destinationViewController;
destinationVC.player = self.bckMusicPlayer.player;
}
}
}
Can't you go the other way round? Why do you have to create an instance of MJDetailViewController? Let the segue do it for you and then in prepareForSegue: you grab the destination controller and pass it the required data. Don't fight the framework.
Alternatively you could override:
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender
And prevent the segue in case it is targeting MJDetailViewController and manually transfer to your controller and present it.
No, you can't do that. Segues (other than unwinds) always instantiate new controllers. You either need to not instantiate it in the viewDidLoad, or move to the new controller in code rather than using a segue.
When doing a modal segue, does the originating ViewController get discarded after the segue is performed? I am setting the destination controller's delegate to the source ViewController, but when the destination ViewController.viewDidLoad, the self.delegate is nil...
The following code will produce the log message "ListViewController.viewDidLoad: My delegate is nil :("
[Source] MapViewController:
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:#"mapToList"]){
NSLog(#"MapViewController.prepareForSegue: Segue mapToList being called, setting LisViewController's delegate to myself");
[segue.destinationViewController setDelegate:self];
if(!self){
NSLog(#"MapViewController.prepareForSegue: I am nil.");
} else {
NSLog(#"MapViewController.prepareForSegue: I am NOT nil.");
}
}
}
[Destination] ListViewController:
- (void)viewDidLoad
{
[super viewDidLoad];
if(!self.delegate){
NSLog(#"ListViewController.viewDidLoad: My delegate is nil :(");
} else {
NSLog(#"ListViewController.viewDidLoad: My delegate populated");
}
}
Your code seems correct, the only thing I have done differently is test this in a skeleton framework I have that is a tableviewcontroller nested in a navigationcontroller. I just tested with the following code and it works fine for me:
RootViewController .h:
#interface RootTableViewController : UITableViewController <newTest>
Prepare for Segue (in rootViewController):
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"segueToModalView"]){
[segue.destinationViewController setDelegate:self];
}
}
Top of Modal View Controller .h:
#protocol newTest <NSObject>
-(void) hello;
#end
Property Declaration in Modal View:
#property (nonatomic, strong) id <newTest> delegate;
ViewDidLoad in Modal View:
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"%#", self.delegate);
}
My NSLog of self.delegate properly prints out and my code appears to be more or less the same as yours. Is your property declared correctly?
This is an old question but I came upon it when running into the same issue myself. Couple of things here:
To the guy who didn't understand why someone would want to use a Nav controller with a modal display - its to get the benefit of the nav bar without having to embed a UINavigationBar into your own view controller like tw airball did.
To solve the problem without resorting to what tw airball did remember that the destination view controller for the segue in this case is the navigation controller...not the view controller embedded in the nav.
So the fix is in your prepareForSeque:
UINavigationController *navController = segue.destinationViewController;
MyRealDestViewController *myRealDestViewController = (MyRealDestViewController)navController.topViewController;
myRealDestViewController.delegate = self;
If the segue is to a NavigationController then the destinationViewController loses the delegate.
I got around this problem by having the modal segue into the destinationViewController, and then adding NavigationBar and Bar Buttons to simulate the navigation controller (I assume you wrapped the destinationViewController in a NavigationController for the "done" and "cancel" buttons).
Set the delegate as normal in the rootViewController:
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"segueToModalView"]){
[segue.destinationViewController setDelegate:self];
}
}
Hope that helps
I'm using storyboard in my ipad application and successfully able to do transitions, use segues etc.
Currently I am showing pop over view controller on click of a button. I want to detect when the pop over dismisses.
How can I do it?
Here is what I did:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"popover"])
{
UIStoryboardPopoverSegue *pop = (UIStoryboardPopoverSegue*)segue;
pop.popoverController.delegate = self;
}
}
UIPopoverController
Now with my revelation that you're talking about a UIPopoverController, here are the steps:
Setup the UIPopoverController with an appropriate delegate (I'm assuming the "sender" view controller)
Have your "sender" conform to the UIPopoverControllerDelegate
Implement the – popoverControllerDidDismissPopover: message and have any detection logic here
Implement - prepareForSegue:sender: and use the segue's destinationController to both get a reference and set the delegate, something like below:
((MyViewController*)segue.destinationController).delegate = self;
Modal View Controller
Add a delegate to the view controller that is being presented
Name your segue if you haven't already
Have your base view controller implement - prepareForSegue:sender: (refer to the UIViewController documentation)
Assign the sending view controller as the modal view controller's delegate in prepareForSegue:sender:
Call a desired method on the delegate immediately before or after you call dismissModalViewControllerAnimated:
That is how I would approach this. I would also recommend having a formal protocol to conform your sending view controller with.
Create a segue in view controller:
#property (strong, nonatomic) UIStoryboardPopoverSegue* popSegue;
In XIB, create an identifier called "popover" for the view.
In Interface, write the following code:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if( [[segue identifier] isEqualToString:#"popover"] )
{
//[[segue destinationViewController] setDelegate:self];
NSLog(#"%#",[[segue destinationViewController] viewControllers]);
self.popSegue = (UIStoryboardPopoverSegue*)segue;
.
.
.
}
Write the following code to dismiss the pop over by coding:
[self.popSegue.popoverController dismissPopoverAnimated:YES];
Since UIStoryboardPopoverSegueis deprecated in iOS 9, you can use a UIStoryboardPopoverPresentationSegue.
Then in prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)you can set the delegate like this:
Swift:
if let identifier = segue.identifier where identifier == "showPopover" {
let destVC = segue.destinationViewController as! UIViewController
destVC.popoverPresentationController?.delegate = self
}
An Objective-C code for the question is below.
if ([segue.identifier isEqualToString:#"home_login"])
{
UIViewController *dest = segue.destinationViewController;
dest.popoverPresentationController.delegate = self;
}
- (BOOL) popoverPresentationControllerShouldDismissPopover:(UIPopoverPresentationController *)popoverPresentationController
{
return NO;
}