Базовые данные отношение один ко многим и табличное представление - PullRequest
1 голос
/ 21 ноября 2011

Ну, я бьюсь над этим уже некоторое время, определенно, в течение недели, и я просто не знаю, где я иду не так. Ваша помощь будет принята с благодарностью !!

Вот идея приложения. У меня есть две сущности, смоделированные в основных данных. У сущности магната есть связь «один ко многим» с речью, поэтому каждый магнат может иметь более одной речи. В сущности Tycoon отношение называется TycoonToSpeech, а в сущности Speech отношение к Tycoon называется SpeechToTycoon (это отношение не один ко многим).

Проблема 1: Я думал, что правильно создал свои отношения в файле appdelegate.m, но я не уверен. Вот код в моем приложении для создания контента. Должен ли я сделать что-нибудь еще с NSMutableSets ???

NSManagedObjectContext *context = [self managedObjectContext];


NSEntityDescription *tycoonEntity = [NSEntityDescription entityForName:@"Tycoon" inManagedObjectContext:context];

NSManagedObject *steve = [NSEntityDescription insertNewObjectForEntityForName:[tycoonEntity name] inManagedObjectContext:context];

[steve setValue:@"Steve Jobs" forKey:@"Name"];
int orgId = [steve hash];
[steve setValue:[NSNumber numberWithInt:orgId] forKey:@"Id"];

NSManagedObject *warren = [NSEntityDescription insertNewObjectForEntityForName:[tycoonEntity name] inManagedObjectContext:context];
[warren setValue:@"Warren Buffet" forKey:@"Name"];
int orgId2 = [warren hash];
[warren setValue:[NSNumber numberWithInt:orgId2] forKey:@"Id"];


NSEntityDescription *speechEntity = [NSEntityDescription entityForName:@"Speech" inManagedObjectContext:context];
NSManagedObject *stanford = [NSEntityDescription insertNewObjectForEntityForName:[speechEntity name] inManagedObjectContext:context];
[stanford setValue:[NSNumber numberWithInt:[stanford hash]] forKey:@"speechId"];
[stanford setValue:@"Stanford" forKey:@"speechName"];

NSManagedObject *wwdc = [NSEntityDescription insertNewObjectForEntityForName:[speechEntity name] inManagedObjectContext:context];
[wwdc setValue:[NSNumber numberWithInt:[wwdc hash]] forKey:@"speechId"];
[wwdc setValue:@"WWDC" forKey:@"speechName"];

NSManagedObject *shareHolder = [NSEntityDescription insertNewObjectForEntityForName:[speechEntity name] inManagedObjectContext:context];
[shareHolder setValue:[NSNumber numberWithInt:[shareHolder hash]] forKey:@"speechId"];
[shareHolder setValue:@"Shareholder's Meeting" forKey:@"speechName"];

NSManagedObject *columbia = [NSEntityDescription insertNewObjectForEntityForName:[speechEntity name] inManagedObjectContext:context];
[columbia setValue:[NSNumber numberWithInt:[columbia hash]] forKey:@"speechId"];
[columbia setValue:@"Columbia" forKey:@"speechName"];


NSMutableSet *jobsSpeeches = [steve mutableSetValueForKey:@"TycoonToSpeech"];
[jobsSpeeches addObject:stanford];
[jobsSpeeches addObject:wwdc];


NSMutableSet *warrenSpeeches = [warren mutableSetValueForKey:@"TycoonToSpeech"];
[warrenSpeeches addObject:shareHolder];
[warrenSpeeches addObject:columbia];

Проблема 2: Основная и действительно раздражающая проблема, я не знаю, как заставить didSelectRowAtIndexPath в моем контроллере корневого представления работать так, чтобы, когда я нажимаю на «Стива Джобса» в табличном представлении, он поднимал только его речи и когда я нажимаю на Уоррена Баффета, он вызывает только его речи. Я ударился головой о стену, пытаясь понять это правильно. В настоящий момент, когда я нажимаю на Стива Джобса или Уоррена Баффета, я перехожу к новому табличному представлению, отображающему «Стив Джобс» и «Уоррен Баффет», так что в основном это тот же самый табличный вид, что и у оригинала !!! Вот мой код для didSelectRowAtIndexPath.

    // Create a new table view of this very same class.
    RootViewController *rootViewController = [[RootViewController alloc]
                                              initWithStyle:UITableViewStylePlain];

    // Pass the managed object context
    rootViewController.context = self.context;
    NSPredicate *predicate = nil;
    // Get the object the user selected from the array
    NSManagedObject *selectedObject = [entityArray objectAtIndex:indexPath.row];


    rootViewController.entityName = @"Speeches";

    // Create a query predicate to find all child objects of the selected object. 
    predicate = [NSPredicate predicateWithFormat:@"SpeechToTycoon == %@", selectedObject];


    [rootViewController setEntitySearchPredicate:predicate];

    //Push the new table view on the stack
    [self.navigationController pushViewController:rootViewController animated:YES];
    [rootViewController release];

Недавно отредактировано здесь **

ОК, вот основной код в моем файле SpeechViewController.m. Метод numberOfRowsInSection, кажется, работает, но реальная проблема - это cellForRowAtIndexPath, которая является проблемой. Я знаю, что, вероятно, делаю что-то очень глупое, но я не знаю, что это такое.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
NSArray *speeches = (NSArray *)tycoon.TycoonToSpeech;
// Configure the cell...
cell.textLabel.text = [[speeches objectAtIndex:indexPath.row] retain];
return cell;

}

Новый контроллер представления речи к контроллеру языковых точек

Я добавил это в делегат приложения, чтобы добавить объекты в мою сущность языковых точек.

NSEntityDescription *langPointEntity = [NSEntityDescription entityForName:@"LanguagePoints" inManagedObjectContext:context];

NSManagedObject *pointOne = [NSEntityDescription insertNewObjectForEntityForName:[langPointEntity name] inManagedObjectContext:context];
[pointOne setValue:[NSNumber numberWithInt:[pointOne hash]] forKey:@"LangId"];
[pointOne setValue:@"Drop out" forKey:@"LangPoint"];
[pointOne setValue:stanford forKey:@"LanguagePointsToSpeech"];

Затем в моем файле speechViewController.m я создал NSArray с именем speechInfo, тогда вот мой метод didSelectRowAtIndexPath.

LangPointsViewController *langPointsView = [[LangPointsViewController alloc] initWithNibName:@"LangPointsViewController" bundle:[NSBundle mainBundle]];
Speech *speech = [speechInfo objectAtIndex:indexPath.row];
langPointsView.speech = speech;
[self.navigationController pushViewController:langPointsView animated:YES];
[langPointsView release];

Я думаю, что здесь все идет не так, я не думаю, что я получаю объект здесь правильно, потому что я делаю NSLog для речи. Имя в LangPointsViewController, и он появляется пустым, где, как будто я делаю NSLog для tycoon.Name в SpeechViewController он печатает имя человека, на которого я нажал. Это NSArray, это неправильно.

1 Ответ

9 голосов
/ 21 ноября 2011

Задача # 1

Сначала ваши отношения плохие.Они должны начинаться со строчной буквы, и они должны просто описать то, что находится на другой стороне отношений.Вы уже знаете, где вы находитесь.Назовите их tycoon и speeches соответственно.Во-вторых, поскольку отношения являются двунаправленными, вы можете легче создавать свои данные, см. Следующее:

NSManagedObjectContext *context = [self managedObjectContext];

NSManagedObject *steve = [NSEntityDescription insertNewObjectForEntityForName:@"Tycoon" inManagedObjectContext:context];

[steve setValue:@"Steve Jobs" forKey:@"Name"];
int orgId = [steve hash];
[steve setValue:[NSNumber numberWithInt:orgId] forKey:@"Id"];

NSManagedObject *warren = [NSEntityDescription insertNewObjectForEntityForName:[tycoonEntity name] inManagedObjectContext:context];
[warren setValue:@"Warren Buffet" forKey:@"Name"];
int orgId2 = [warren hash];
[warren setValue:[NSNumber numberWithInt:orgId2] forKey:@"Id"];

NSManagedObject *stanford = [NSEntityDescription insertNewObjectForEntityForName:@"Speech" inManagedObjectContext:context];
[stanford setValue:[NSNumber numberWithInt:[stanford hash]] forKey:@"speechId"];
[stanford setValue:@"Stanford" forKey:@"speechName"];
[stanford setValue:warren forKey:@"tycoon"];

NSManagedObject *wwdc = [NSEntityDescription insertNewObjectForEntityForName:[speechEntity name] inManagedObjectContext:context];
[wwdc setValue:[NSNumber numberWithInt:[wwdc hash]] forKey:@"speechId"];
[wwdc setValue:@"WWDC" forKey:@"speechName"];
[wwdc setValue:warren forKey:@"tycoon"];

NSManagedObject *shareHolder = [NSEntityDescription insertNewObjectForEntityForName:[speechEntity name] inManagedObjectContext:context];
[shareHolder setValue:[NSNumber numberWithInt:[shareHolder hash]] forKey:@"speechId"];
[shareHolder setValue:@"Shareholder's Meeting" forKey:@"speechName"];
[shareHolder setValue:warren forKey:@"tycoon"];

NSManagedObject *columbia = [NSEntityDescription insertNewObjectForEntityForName:[speechEntity name] inManagedObjectContext:context];
[columbia setValue:[NSNumber numberWithInt:[columbia hash]] forKey:@"speechId"];
[columbia setValue:@"Columbia" forKey:@"speechName"];
[columbia setValue:warren forKey:@"tycoon"];

Обратите внимание, что я устанавливаю отношение со стороны Speech.Базовые данные будут обрабатывать другую сторону.

Задача № 2

Не глядя на весь контроллер корневого представления, трудно сказать, что вы делаете «неправильно».Однако я бы не стал повторно использовать контроллер представления для отображения двух разных типов объектов.Это плохая форма и ведет к краже кода.

Вместо этого я бы создал второй «главный» контроллер вида, предназначенный для обработки речей.Затем вы можете просто передать магната, и ваш SpeechViewController может запустить только из массива, полученного из отношений, или он может создать свой извлеченный контроллер результатов изнутри.Еще большее значение имеет возможность настраивать свои ячейки, заголовок и т. Д. В соответствии с отображаемыми данными.

Задача № 3

Сначала добавьте комментарий о новом коде, который вы добавили:

SpeechViewController *speechView = [[SpeechViewController alloc] initWithNibName:@"SpeechViewController" bundle:[NSBundle mainBundle]];
Tycoon *tycoons = (Tycoon *)[fetchedResultsController objectAtIndexPath:indexPath];
speechView.tycoons = tycoons;
[self.navigationController pushViewController:speechView animated:YES];

Существует ZERO причина для изгнания из -[NSFetchedResultsController objectAtIndexPath:].Любой метод, который возвращает id, может быть назначен любому указателю.

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

Теперь, оставив это в стороне, ваш код в порядке.Я бы назвал свойство «магнат» вместо «магнат», потому что вы передаете одну сущность магната.

На стороне SpeechViewController теперь вы можете получить доступ к речам, связанным с этой сущностью магната, и вывести их на экран, однакоВы выбираете.

В чем проблема, которую вы видите?

Задача # 4

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (!cell) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }
    NSSet *speechesSet = [[self tycoon] valueForKey:@"TycoonToSpeech"];
    // Sort the speeches
    NSSortDescriptor *nameSort = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
    NSArray *sortedSpeeches = [speechesSet sortedArrayUsingDescriptors:[NSArray arrayWithObject:nameSort]];
    // Configure the cell...
    id speech = [sortedSpeeches objectAtIndex:[indexPath row]];
    [[cell textLabel] setText:[speech valueForKey:@"name"]];

    return cell;
}

Хорошо, начиная с верха.Во-первых, отношения вернут NSSet, а не NSArray.Результаты отношений неупорядочены.Поэтому мы берем результаты как NSSet и затем сортируем их.Я догадался, что у вашей Speech сущности есть атрибут name.

Как только мы их отсортируем, мы сможем взять элемент в строке.Это вернет Speech сущность.Я назначил его id, потому что мы все равно будем использовать KVC.

Наконец, я использую KVC, чтобы получить атрибут name от объекта Speech и назначить его для textLabel изячейка.

Что касается вашего -tableView: numberOfRowsInSection:, то этот гораздо проще:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [[[self tycoon] valueForKey:@"TycoonToSpeech"] count];
}

Поскольку у вас есть только один раздел, вам нужно только вернуть количество речей.

Я упоминал ранее, что имена ваших атрибутов нуждаются в некоторой работе.Поскольку Tycoon - это просто объект, а TycoonToSpeech - это просто свойство этого объекта, свойство действительно должно называться speeches, поскольку оно не более особенное, чем любое другое свойство объекта.Имя атрибута лучше течет в вашем коде и облегчает его использование.Кроме того, в соглашениях об именах Objective-C атрибуты, называемые свойствами, должны начинаться со строчной буквы.

...