简体   繁体   中英

Problems passing data to a DetailViewController from TableView in iOS5

I would appreciate any help in solving my problem that has had me pulling my hair out for the last day!

Disclaimer 1 I'm an Objective-C noob - so the answer to this may be very simple (I'm coming from years of PHP). Please be nice.

Disclaimer 2 Yes, I have extensively 'Googled' and looked at Apple Documentation - but have failed to come to an understanding of my problem.

Summary

I am trying to pass a value selected in a table view to another controller from a singleton. I am finding that the viewDidLoad method in the second view controller is completing before the tableView: didSelectRowAtIndexPath: method which triggered the segue. This is leading to the second view controller access 'stale' data.

Detail

I have set up a project with a very simple StoryBoard using ARC targeting iOS5.

故事板

I have created a singleton to store a NSString between view controllers.

Singleton.h

#import <Foundation/Foundation.h>
@interface Singleton : NSObject
@property (strong, nonatomic) NSString *sharedString;
+ (id)singletonManager;
@end

Singleton.m

#import "Singleton.h"

static Singleton *sharedInstance = nil;

    @implementation Singleton
    @synthesize sharedString = _sharedString;
    +(id)singletonManager
    {
        @synchronized(self){
            if (sharedInstance == nil)
                sharedInstance = [[self alloc] init];
        }
        return sharedInstance;
    }
    -(id) init {
        if (self = [super init]) {
            _sharedString = [[NSString alloc] initWithString:@"Initialization String"];
        }
        return self;
    }

    @end

The TableView in the story board is linked to the following TableViewController

TableViewController.h

#import <UIKit/UIKit.h>

@interface TableViewController : UITableViewController
@end

TableViewController.m

#import "TableViewController.h"
#import "Singleton.h"

@interface TableViewController ()
@property NSArray *tableData;
@end

@implementation TableViewController
@synthesize tableData;

- (id)initWithStyle:(UITableViewStyle)style
{
    self = [super initWithStyle:style];
    if (self) {
    }
    return self;
}

- (void)viewDidLoad
{

    [super viewDidLoad];
    self.tableData = [NSArray arrayWithObjects:@"First", @"Second", @"Third", nil];
}

- (void)viewDidUnload
{
    [super viewDidUnload];
}


- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.
    return [self.tableData count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    // Configure the cell...
    cell.textLabel.text = [self.tableData objectAtIndex:indexPath.row];
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{    
    NSLog(@"TableViewController: didSelectRowAtIndexPath start");
    Singleton *sharedInstance = [Singleton singletonManager];
    sharedInstance.sharedString = [self.tableData objectAtIndex:indexPath.row];
    NSLog(@"TableViewController: Finished storing data into shared string: %@", sharedInstance.sharedString);
    NSLog(@"TableViewController: didSelectRowAtIndexPath start");
}

@end

The table cell is segue-ed (push) to a basic ViewController with a single label.

DetailViewController.h

#import <UIKit/UIKit.h>

@interface DetailViewController : UIViewController
@property (strong, nonatomic) IBOutlet UILabel *displayLabel;

@end

DetailViewController.m

#import "DetailViewController.h"
#import "Singleton.h"

@interface DetailViewController ()

@end

@implementation DetailViewController
@synthesize displayLabel = _displayLabel;

- (void)viewDidLoad
{
    [super viewDidLoad];
    NSLog(@"DetailViewController: viewDidLoad start");
    Singleton *sharedInstance = [Singleton singletonManager];
    self.displayLabel.text = sharedInstance.sharedString;
    NSLog(@"displayLabel.text: %@", self.displayLabel.text);
    NSLog(@"DetailViewController: viewDidLoad end");
}

- (void)viewDidUnload
{
    [self setDisplayLabel:nil];
    [super viewDidUnload];
}

@end

When a table cell is selected, I am wanting

  • the TableViewController to set a value in the shared singleton NSString
  • to commence the segue to the Detail View
  • the DetailViewController to get the value in the shared singleton NSString and display it in a label
  • the Detail View to render to the screen

Unfortunately this is not working as expected with my code. If I look at the NSLog details in the console I can see that the detail view's viewDidLoad function is actually finishing before the initiating table view method ( didSelectRowAtIndexPath ).

2012-05-10 23:17:45.756 TestSingleton[12105:f803] DetailViewController: viewDidLoad start
2012-05-10 23:17:45.758 TestSingleton[12105:f803] displayLabel.text: Initialization String
2012-05-10 23:17:45.759 TestSingleton[12105:f803] DetailViewController: viewDidLoad end
2012-05-10 23:17:45.763 TestSingleton[12105:f803] TableViewController: didSelectRowAtIndexPath start
2012-05-10 23:17:45.764 TestSingleton[12105:f803] TableViewController: Finished storing data into shared string: First
2012-05-10 23:17:45.765 TestSingleton[12105:f803] TableViewController: didSelectRowAtIndexPath start

Arrrrggghh!

If I place the setting of the label text in the viewDidAppear method of the detail view, the correct value from shared singleton NSString will be displayed. With this approach, however, you can actually see the app changing the value of the text label (eg. if you click on the first row, when the detail view screen loads you can SEE the label change from "Initialization String" to "First").

Can somebody please explain how I can pass a shared value from a singleton to another view and have it available to the second view controller before the view is actually rendered to screen .

If any clarification is required - please let me know.

I'm sure this has something to do with my failure to appreciate the subtleties of the view lifecycle.

UPDATE

Thanks to *8vius.

My new method in TableViewController.m which solves my problem:

- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if([segue.identifier isEqualToString:@"mySegue"]) {
        NSLog(@"Preparing for Segue to detail view");
        Singleton *sharedInstance = [Singleton singletonManager];
        NSIndexPath *selectedRow = [self.tableView indexPathForSelectedRow];
        sharedInstance.sharedString = [self.tableData objectAtIndex:selectedRow.row];
        NSLog(@"TableViewController: Finished storing data into shared string: %@", sharedInstance.sharedString);

    }
}

In your destination view controller set up a property (public) so you can set the value you wish to pass in. Implement prepareForSegue in your origin view controller and import the header of your destination view controller.

To get the indexPath of a table view cell you should have an IBOutlet set to your table view, this way you can get the indexPath using the indexPathForCell method in your prepareForSegue method.

In your prepareForSegue you should have something like this:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
  NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
  if ([segue.identifier isEqualToString:@"[identifier for your segue]"]) {
     [segue.destinationViewController setValueIWantToSet:value];
  }
}

You could also set the value of the label text here, if you wish.

The reason your viewDidLoad finishes before is because iOS sets up the destination view controller before transitioning to it, using the prepareForSegue method guarantees that your destination view controller is already instantiated and set up, so you can set the values you wish.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM