简体   繁体   中英

How can I populate a sparse UITableView from a fetched results object?

In my sweet app you can use two kinds of items from a list of items. There are equipable items, and bag items. When you navigate to the inventory screen, there are two sections of a table view, each with 4 cells. Four cells for equipped items, and four cells for items in your "bag". If you don't have anything equipped, the cells just say "empty".

How can I populate table cells conditionally based on the shape of my results object?

Here is the code I wish I had:

- (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];
  }

  // this returns 'nil' if the index path was out of bounds
  NSManagedObject * item = [self.fetchedResultsController carefullyGetObjectAtIndexPath:indexPath];

  if (nil == item) {
    cell.textLabel.text = @"empty"; 

  } else {
    cell.textLabel.text = [item valueForKey:@"name"];

  }

} 

Some problems I've encountered trying to implement this 'careful' objectAtIndexPath:

I tried bounds checking, but I'm having trouble because the fetchedArray is flat, while the conceptual model I am trying to navigate is not flat: rows are 'in' sections.

I tried using a try/catch, but then I remembered that the out-of-bounds elements are undefined, not nil... so sometimes I get an error that I can catch and react to, and sometimes I just get random interesting stuff. The setup I have now:

@try {

  NSManagedObject *managedObject = [self.fetchedResultsController objectAtIndexPath:indexPath];

  return managedObject;
}
@catch (NSException * e) {
  return nil;
}

consistently gives me 5 entries, 4 in the right places and 1 odd-ball which is a repeat from the other section.

Not sure how to navigate the fetched results manually. I'm playing with four items, 2 of each kind. I had hoped it would be as easy as a doubly nested array, with sections on the outside and rows on the in. It seems that things are trickier than this. When I inspect the fetchedResults NSArray, I see that it is flat. All the "bag" items are first, and the "equipable" come next. I don't imagine I can depend on this ordering, though, so I don't feel safe making a mapping function to go from indexPath section to array index. (I could be convinced to feel safe about this).

An alternative I thought of involves populating my inventory with 8 'empty' items that would be returned as place holder results when the sections don't get completely filled. This doesn't sit well with me though.

I realize why this isn't working: tables aren't supposed to have 'empty' rows. But I want my table to have empty rows!

UPDATE

Here's the method I'm using for now...

- (NSManagedObject*) carefullyGetObjectAtIndexPath:(NSIndexPath*)indexPath 
{
  NSLog(@"in %@ %s", [self class], _cmd);

  NSString * desiredType = (indexPath.section == BAG_SECTION)? @"bag" : @"equipable";
  NSArray * fetchedObjects = [self.fetchedResultsController fetchedObjects];
  NSInteger sectionStartIndex = -1;

  // iterate until you hit a type you like
  NSUInteger i, count = [fetchedObjects count];
  for (i = 0; i < count; i++) {
    NSObject * obj = [fetchedObjects objectAtIndex:i];
    NSString * fetchedType = (NSString*)[obj valueForKey:@"type"];

    if ([desiredType isEqualToString:fetchedType]) {
      sectionStartIndex = i;
      break;
    }
  }

  if (-1 == sectionStartIndex) {
    // maybe there aren't any of that type of item

  } else {

    NSInteger calculatedEntityIndex = sectionStartIndex + indexPath.row;
    NSLog(@"calculated index %d", calculatedEntityIndex);

    // if we are still in the array
    if (calculatedEntityIndex < [fetchedObjects count]) {

      // then we can get the object
      NSManagedObject * entity = [fetchedObjects objectAtIndex:calculatedEntityIndex];

      // and if we are still in the right section
      NSString * typeForEntityAtIndex = [entity valueForKey:@"type"];

      if ([desiredType isEqualToString:typeForEntityAtIndex]) {
        // then this is what we wanted

        return entity;
      }
    }
  }

  return nil;   
}

I just really don't feel I should have to iterate over something like this...

z.

Full disclosure: this is a term project, but the project is to make the app by any means necessary. This includes third party libraries, asking questions on stackoverflow, &c... The goal of this term project is to experience working on something bigger, like a sweet app, not to learn objective-c.

Ziggy,

The NSFetchedResultsController has many limitations. If you cannot make your schema meet its exacting requirements and it appears you probably haven't, then you should construct your tableView out of multiple NSFetchRequest s. It is straightforward. Most of the interesting tableView s do just this. Core Data really works best when you perform larger fetches and then refine the search using the set and filter operations on the collection classes.

tableView s can have empty rows. You just have to coordinate this with custom code in your various delegate and cell configuration code.

You have a lot of options to solve your problem. It appears you are unhappy with a quite limited framework API. You're a budding programmer. Roll your own.

Andrew

PS You have a curious goal to write a Mac OS X/iOS app without needing to learn Objective-C. You actually have chosen the one technology in the framework, Core Data, that exercises Objective-C to its maximum capability. (In my opinion, as an educator of iOS programmers, your education would probably be better served learning the intricacies of a very different persistence model, Core Data's model, than retreating to mundane, mainstream SQL solution.) If you would prefer to use a different language, MacRuby/RubyMotion is quite effective.

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