Вопрос по uitableview, singleton, segue, objective-c, ios – Проблемы при передаче данных в DetailViewController из TableView в iOS5

4

Я был бы признателен за любую помощь в решении моей проблемы, которая заставила меня выдернуть волосы за последний день!

Disclaimer 1  Я новичок в Objective-C - так что ответ на этот вопрос может быть очень простым (я имею в виду годы PHP). Пожалуйста будь вежлив.

Disclaimer 2  Да, я широко "гуглил" и посмотрел на документацию Apple - но так и не смог понять мою проблему.

Summary

Я пытаюсь передать значение, выбранное в табличном представлении, другому контроллеру из синглтона. Я считаю, чтоviewDidLoad метод во втором контроллере представления завершается доtableView: didSelectRowAtIndexPath: метод, который вызвал переход. Это приводит к тому, что второй вид контроллера доступа «устаревший» данные.

Detail

Я создал проект с очень простой StoryBoard, используя ARC для iOS5.

StoryBoard

Я создал синглтон для хранения строки NSString между контроллерами представления.

Singleton.h

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

Singleton.m

<code>#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
</code>

TableView в раскадровке связан со следующим TableViewController

TableViewController.h

<code>#import <UIKit/UIKit.h>

@interface TableViewController : UITableViewController
@end
</code>

TableViewController.m

<code>#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
</code>

Ячейка таблицы передается (push) на базовый ViewController с одной меткой.

DetailViewController.h

<code>#import <UIKit/UIKit.h>

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

@end
</code>

DetailViewController.m

<code>#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
</code>

Когда ячейка таблицы выбрана, я хочу

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

К сожалению, это не работает так, как ожидалось с моим кодом. Если я посмотрю подробности NSLog в консоли, то увижу, что подробный видviewDidLoad функция на самом деле завершается до инициации метода табличного представления (didSelectRowAtIndexPath).

<code>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
</code>

Arrrrggghh!

Если я помещу настройку текста метки вviewDidAppear метод подробного представления, будет отображаться правильное значение из общего синглтона NSString. Однако при таком подходе вы фактически можете увидеть, как приложение меняет значение текстовой метки (например, если вы щелкнете по первой строке, когда загружается экран подробного представления, вы можете ВИДЕТЬ, что метка изменилась с & quot; Initialization String & quot; ; Первый & Quot;).

Может ли кто-нибудь объяснить, как я могу передать общее значение из одиночного в другое представление и сделать его доступным для второго контроллера представленияbefore the view is actually rendered to screen.

Если какие-либо разъяснения необходимы - пожалуйста, дайте мне знать.

Я уверен, что это как-то связано с моей неспособностью оценить тонкости жизненного цикла представления.

UPDATE

Благодаря * 8vius.

Мой новый метод вTableViewController.m которая решает мою проблему:

<code>- (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);

    }
}
</code>

Ваш Ответ

1   ответ
6

В вашем контроллере представления назначения установите свойство (public), чтобы вы могли установить значение, которое вы хотите передать. РеализацияprepareForSegue в вашем контроллере представления источника и импортируйте заголовок вашего контроллера представления назначения.

Чтобы получить indexPath ячейки табличного представления, в вашем табличном представлении должен быть установлен IBOutlet. Таким образом, вы можете получить indexPath с помощью метода indexPathForCell в вашем методе prepareForSegue.

В вашем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];
  }
}

Вы также можете установить значение текста метки здесь, если хотите.

Причина вашаviewDidLoad заканчивается раньше, потому что iOS настраивает контроллер представления назначения перед переходом к нему, используяprepareForSegue Метод гарантирует, что ваш конечный контроллер вида уже создан и настроен, так что вы можете установить нужные значения.

Спасибо 8vius. Вопрос дополнен моим окончательным решением на основе вашего предложения. So Over It
Действительно полезно. я могу достичь этого через didSelectRowAtIndexPath?
Еще одно предложение для вас, если вы начинаете с iOS. Из iTunes вы можете бесплатно скачать курс по разработке приложений для iPad и iPhone от Стэнфордского университета, это отличная помощь для начала работы.
Благодарю. При тестировании я вижу, что prepareForSegue запускается до подробного просмотра.viewDidLoad, Я все еще не до конца уверен, как передать выбранный indexPath с использованием prepareForSegue. So Over It
Не вставляйте код в комментарии, просто отредактируйте свой вопрос. Если вы хотите передать путь к индексу, он будет таким же, установите свойство для получения значения пути к индексу в заголовке вашего конечного контроллера представления и установите его из метода prepareForSegue. Отправитель в методе будет ячейкой, которая была выбрана, так что вы можете получить индексный путь из этого. Для этого вы должны привести отправителя к UITableViewCell, я отредактирую свой ответ, чтобы отразить это.

Похожие вопросы