简体   繁体   English

从iOS5中的TableView将数据传递到DetailViewController时出现问题

[英]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). 免责声明1我是一个Objective-C菜鸟 - 所以答案可能非常简单(我来自多年的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. 免责声明2是的,我已经广泛“谷歌搜索”并查看了Apple文档 - 但未能理解我的问题。

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. 我发现第二个视图控制器中的viewDidLoad方法在tableView:didSelectRowAtIndexPath:方法之前完成,该方法触发了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. 我使用针对iOS5的ARC设置了一个非常简单的StoryBoard项目。

故事板

I have created a singleton to store a NSString between view controllers. 我创建了一个单例来在视图控制器之间存储NSString。

Singleton.h Singleton.h

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

Singleton.m 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 故事板中的TableView链接到以下TableViewController

TableViewController.h TableViewController.h

#import <UIKit/UIKit.h>

@interface TableViewController : UITableViewController
@end

TableViewController.m 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. 表格单元格被推送(推送)到具有单个标签的基本ViewController。

DetailViewController.h DetailViewController.h

#import <UIKit/UIKit.h>

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

@end

DetailViewController.m 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 TableViewController在共享单例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 DetailViewController获取共享单例NSString中的值并将其显示在标签中
  • 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 ). 如果我在控制台中查看NSLog详细信息,我可以看到详细视图的viewDidLoad函数实际上是在启动表视图方法( 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! 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. 如果我将标签文本的设置放在详细视图的viewDidAppear方法中,则将显示共享单例NSString中的正确值。 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 UPDATE

Thanks to *8vius. 感谢* 8vius。

My new method in TableViewController.m which solves my problem: 我在TableViewController.m中的新方法解决了我的问题:

- (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. 在目标视图控制器中设置属性(公共),以便设置要传入的值。在源视图控制器中实现prepareForSegue并导入目标视图控制器的标头。

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. 要获取表视图单元的indexPath,您应该将IBOutlet设置为表视图,这样您就可以使用prepareForSegue方法中的indexPathForCell方法获取indexPath。

In your prepareForSegue you should have something like this: 在你的prepareForSegue你应该有这样的东西:

- (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. 之前viewDidLoad完成的原因是因为iOS在转换到目标视图控制器之前设置了目标视图控制器,使用prepareForSegue方法可以保证目标视图控制器已经实例化并设置,因此您可以设置所需的值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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