سؤال

Since switching to storyboards, I load a view controller via

[self performSegueWithIdentifier:@"identifier" sender:self]

This works perfectly. Now, if I want to set any properties on the destination view controllers, I implement the method prepareForSegue:sender: and set what properties I need to set. Everything works as expected without any problems.

Ever since I starting using this approach over the old

MyViewController *vc = ....
vc.prop = @"value";
[self.navigationController pushViewController:vc];

I've felt that passing parameters to the destination view controller is a little hacky, in particular if the value you're trying to set is not just a static value.

Lets say for example, I have a button which fetches some data from a server. When the data returns, it creates a new object, and then presents a new view controller to display this object. To do this, I call performSegueWithIdentifier:sender:, but that's the end of it. My object is now deallocated and no longer exists, and I have no way of passing it to the prepareForSegue:sender: method, unless I store it in an instance variable.

This feels pretty horrible, as the object isn't meant to last longer than this action, and has no relation to anything else in my current view controller.

In this situation, I understand that I could quite simply request the data in the new view controller but it's just an example.

My question is, is there another way of doing this without it feeling so hacky? Can I get this data into the destination view controller without storing it in an instance variable?

I know I could still use the old approach, but I'd like to stick with the storyboard methods if I can.

هل كانت مفيدة؟

المحلول

Well the sender parameter of the performSegueWithIdentifier:sender is the same one received by the prepareForSegue:sender. So if you want to send a variable to your prepareForSegue:sender the sender is your friend. In your case:

SomeViewController.m

-(void)aMethodThatDownloadsSomeDataFromServer {
   NSString *exampleData = [self someDataThatIDownloaded];
   [self performSegueWithIdentifier:@"yourSegueIdentifier" sender:exampleData];
}

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
   if(segue.identifier isEqualToString:@"yourSegueIdentifier"]) { 
      if([sender isKindOfClass:[NSString class]]) { //maybe you want to send different objects
        segue.destinationViewController.stringProperty = sender;
      }
      else { 
        segue.destinationViewController.objectPorperty = sender;
      }
   }
}

نصائح أخرى

The accepted solutios is correct but I frequently use another approach when data are shared between more than two segue. I frequently create a singleton class (let's call it APPSession) and I use it as a datamodel, creating and maintaining a session-like structure I can write and read from everywhere in the code.

For complex applications this solution maybe requires too much error prone coding but I've used it succesfully in a lot of different occasions.

APPSession.m

//
//  APPSession.m
//
//  Created by Luca Adamo on 09/07/12.
//  Copyright 2012 ELbuild. All rights reserved.
//

#import "APPSession.h"

@implementation APPSession
@synthesize myProperty;

static APPSession *instance = nil;

// Get the shared instance and create it if necessary.
+ (APPSession *)instance {
    if (instance == nil) {
        instance = [[super allocWithZone:NULL] init];
    }

    return instance;
}

// Private init, it will be called once the first time the singleton is created
- (id)init
{
    self = [super init];

    if (self) {
        // Standard init code goes here
    }

    return self;
}

// This will never be called since the singleton will survive until the app is finished. We keep it for coherence.
-(void)dealloc
{

}

// Avoid new allocations
+ (id)allocWithZone:(NSZone*)zone {
    return [self sharedInstance];
}

// Avoid to create multiple copies of the singleton.
- (id)copyWithZone:(NSZone *)zone {
    return self;
}

APPSession.h

//
//  APPSession.h
//
//  Created by Luca Adamo on 09/07/12.
//  Copyright 2012 ELbuild. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface APPSession : NSObject{

}

@property(nonatomic,retain) NSString* myProperty;

+ (id)sharedInstance;

@end

How to read and write the property myProperty from every part of the app code.

// How to write "MyValue" to myProperty NSString * 
[APPSession instance] setMyProperty:@"myValue"]
// How to read myProperty
NSString * myVCNewProperty = [[APPSession instance] myProperty];

With this mechanism I can safely write for instance a value in the APPSession in the first ViewController, perform a segue to a second one, perform another segue to a third one and use the variable written during the first segue.

It's more or less like a SessionScoped JavaBean in Java EE. Please feel free to point out problems in this approach.

All of these answers are correct, but I've found a pretty cool way of doing this. I've tested only in iOS 7 and iOS 8

After declaring and setting the value of the object you wish to pass, in the prepareForSegue method,

    [segue.destinationViewController setValue:event forKey:@"property"];
//write your property name instead of "property
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top