Differentiating UITableViewCells Using Core Data - objective-c

I have two entities, Note and Tag, that have a many-to-many relationship.
I want to be able to tap on a Note and have a UITableView that is populated with all the Tags that exist, and all the Tags that have a relationship with the Note that was tapped have a UITableViewCellAccessoryCheckmark on the right.
What I've tried doing is fetching two arrays, the array with all the tags and the array with the related tags. Then I did something like this:
for (int i = 0; i < [self.tagArray count]; i++) {
Tag *new = [self.tagArray objectAtIndex:i];
if ([self.all containsObject: new])
{
new.isIn = TRUE;
}
else {
new.isIn = FALSE;
}
}
isIn is a property of Tag, initialized like this:
In Tag.h:
#property BOOL isIn;
In Tag.m:
#synthesize isIn;
I have a feeling this is the wrong approach though. Can anyone think of a better solution?

As per my comment, if you are displaying all of the tags anyway, you don't need to precheck to see if the tag should have a checkmark or not.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// not going to go into basic cell generation logic here
Tag *currentTag = [self.tagArray objectAtIndex:indexPath.row];
if([self.all containsObject: currentTag])
{
// checkmark
}
else
{
// no checkmark
}
return cell;
}

Related

Objective-c best data structure sortable of string and bool

I need to store a list of strings for my items in a TableView and for each string I need to store a Bool value.. I would use an NSDictionary but how can I sort the list of strings alphabetically (using a selector) and sort at the same time the bool values?
I know that exists methods like sortUsingSelector or UsingComparator, but in the NSDictionary I can only sort keys by values so I need the reverse..
Can anyone help me, maybe using another Data Structure?
I would recommend the following data structure:
Use an NSArray of NSDictionaries like so (make it a property):
self.array = #[#{#"String": #"Zusuuuuu", #"bool": #0}, // I am really not creative ;-) Just wanted an unsorted example
#{#"String": #"YourContent", #"bool": #0},
#{#"String": #"YourOtherContent", #"bool": #1}];
You can then sort it like this:
self.array = [self.array sortedArrayUsingComparator:^NSComparisonResult(NSDictionary *aDictionary, NSDictionary *anotherDictionary) {
return [aDictionary[#"String"] compare:anotherDictionary[#"String"]];
}];
If you want to populate your UITableView just do the following:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.array.count; //If you want them all in one section, easiest case
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// ...
// Do all the initialization of your cell
// ...
cell.yourLabel.text = self.array[indexPath.row][#"String"];
cell.yourSwitch.on = ((NSNumber *)self.array[indexPath.row][#"bool"]).boolValue;
return cell;
}

NSInternalInconsistencyException with UITableViewController and a Storyboard-Pop

I integrated GrabKit in my iPhone-App, which is a Library to Grab Photos from social Networks. Now I have the following situation/problem:
I have a UITableViewController - AlbumListViewController.
Then there's a UITableViewCell - AlbumCellViewController.
When you tap on one of these cells - albums with photos
There's a UITableViewController - PhotoListViewController
and a UITableViewCell - PhotoCellViewController.
Everything here works just finde, I can browse albums, choose one, browse the photos in it and then when I choose one of the single photos, there is a PushViewController to my SetPhotoViewController, which displays the selected image in Fullscreen.
Now when I want to use the back-Button of my navigation bar in the SetPhotoViewController, the crashes with the following message:
*** Assertion failure in -[UISectionRowData refreshWithSection:tableView:tableViewRowData:], /SourceCache/UIKit_Sim/UIKit-2372/UITableViewRowData.m:400
2012-10-22 22:49:32.814 Amis[1820:c07] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Failed to allocate data stores for 1073741821 rows in section 1. Consider using fewer rows'
*** First throw call stack:
(0x23de012 0x1b63e7e 0x23dde78 0x15f9f35 0xc8f83b 0xc923c4 0xb56fa2 0xb5692c 0x1690f 0x1cd653f 0x1ce8014 0x1cd87d5 0x2384af5 0x2383f44 0x2383e1b 0x2a9a7e3 0x2a9a668 0xaab65c 0x42fd 0x2b75)
libc++abi.dylib: terminate called throwing an exception
DataSource and Delegate of my two UITableViewControllers are both set to File's Owner.
When I debug trough the code, I can't find any special things, all photos can be loaded, all the arrays are filled with data, there's nothing strange.
Here are the two functions of the PhotoListViewController which get called, as soon as I press the back button on my NavigationController:
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = nil;
NSLog(#"%i", indexPath.row);
NSLog(#"%ii", indexPath.section);
// Extra Cell
if ( indexPath.section > _lastLoadedPageIndex ){
static NSString *extraCellIdentifier = #"ExtraCell";
cell = [tableView dequeueReusableCellWithIdentifier:extraCellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:extraCellIdentifier];
}
cell.textLabel.text = #"load more";
cell.textLabel.font = [UIFont fontWithName:#"System" size:8];
} else {
static NSString *photoCellIdentifier = #"photoCell";
cell = [tableView dequeueReusableCellWithIdentifier:photoCellIdentifier];
if (cell == nil) {
cell = [[[NSBundle mainBundle] loadNibNamed:#"PhotoRowViewController" owner:self options:nil] objectAtIndex:0];
}
}
// setting of the cell is done in method [tableView:willDisplayCell:forRowAtIndexPath:]
return cell;
}
and..
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
// extra cell
if ( [cell.reuseIdentifier isEqualToString:#"ExtraCell"] ){
if ( state == GRKDemoPhotosListStateAllPhotosGrabbed ) {
cell.textLabel.text = [NSString stringWithFormat:#" %d photos", [[_album photos] count] ];
}else {
cell.textLabel.text = [NSString stringWithFormat:#"Loading page %d", _lastLoadedPageIndex+1];
[self fillAlbumWithMorePhotos];
}
} else // Photo cell
{
NSArray * photosAtIndexPath = [self photosForCellAtIndexPath:indexPath];
[(PhotoRowViewController*)cell setPhotos:photosAtIndexPath];
}
}
What am I missing?
Edit: The code of the numberOfRowsInSection-Method:
If I debug it, the first time res is equal 0, the second time the method gets called, res is equal 322121535.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
NSUInteger res = 0;
if ( state == GRKDemoPhotosListStateAllPhotosGrabbed && section == _lastLoadedPageIndex ) {
NSUInteger photosCount = [_album count];
// Number of cells with kNumberOfPhotosPerCell photos
NSUInteger numberOfCompleteCell = (photosCount - section*kNumberOfRowsPerSection*kNumberOfPhotosPerCell) / kNumberOfPhotosPerCell;
// The last cell can contain less than kNumberOfPhotosPerCell photos
NSUInteger thereIsALastCellWithLessThenFourPhotos = (photosCount % kNumberOfPhotosPerCell)?1:0;
// always add an extra cell
res = numberOfCompleteCell + thereIsALastCellWithLessThenFourPhotos +1 ;
} else if ( section > _lastLoadedPageIndex) {
// extra cell
res = 1;
} else res = kNumberOfRowsPerSection;
return res;
}
I'm the developer of GrabKit. Thanks for using it :)
Actually, the controllers in GrabKit are there for demonstration purpose only, they are not designed to be used "as is" in a project, and more specifically : The GRKDemoPhotosList controller was not made to have another controller pushed in the controllers hierarchy.
So what you need is just a little fix to make grabKit demo's controllers fit to your project :)
I guess the problem is the following :
_ in [GRKDemoPhotosList viewWillAppear], the method fillAlbumWithMorePhotos is called.
_ when you pop back from your controller to GRKDemoPhotosList, this method is called one more time, and it generates this bug.
Try to add a flag in the viewWillAppear method, to avoid calling fillAlbumWithMorePhotos twice, I guess it'll work fine.
Feel free to contact me by mail ( pierre.olivier.simonard at gmail.com ) or on twitter ( #pierrotsmnrd ) if you need more informations or help :)

Create a sectioned UITableView using NSFetchedResultsController

I am trying to create a UITableView with two different sections. I know I can group them on an attribute of my managed object. For instance if I'd like to group them per name I'd do:
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:_context
sectionNameKeyPath:#"name"
cacheName:#"uploadProperties"];
And I return the number of secionts like:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [[_fetchedResultsController sections] count];
}
The problem though is that I do not want to group it per attribute (such as the name). I want to group them for specific values, namely one part that has pud_id = 0 and another section that has pud_id > 0.
How can I achieve this? Is it possible to use kind of a where clause? Or can I create a property on my managed object and use this in the sectionNameKeyPath such as:
- (BOOL) hasPudZero {
if (self.pud_id == 0)
return YES;
return NO;
}
??
Thanks for your input!
Perhaps you could create a transient attribute of type NSString that returns one of two strings?
- (NSString *)sectionIdentifier
{
[self willAccessValueForKey:#"sectionIdentifier"];
NSString *tmp = [self primitiveValueForKey:#"sectionIdentifier"];
[self didAccessValueForKey:#"sectionIdentifier"];
if (!tmp) {
if (self.pud_id == 0) {
tmp = #"SectionOne";
} else {
tmp = #"SectionTwo";
}
[self setPrimitiveValue:tmp forKey:#"sectionIdentifier"];
}
return tmp;
}
and then use sectionNameKeyPath:#"sectionIdentifier" when creating your NSFetchedResultsController. Make sure you set the primitive value of sectionIdentifier back to nil if the value of put_id changes.

TitleForHeaderInSection throwing EXC_BAD_ACCESS

I am trying to create a generic UITableView in my iPhone app.
I have a UITableView which populates the data using an array via a SELECT query loop.
I add the data into my array and populate the array in cellForRowAtIndexPath:.
I get the section header using that array and by using a sort method, I put the section headers in Array1.
I would like to have titleForHeaderInSection: work by having section 0 be a static header name and sections 1 and later become generic, meaning the header name will come from Array1.
I am not sure how can I create that logic since the app always throws EXC_BAD_ACCESS with the code below.
My logic: I keep the count of the array in an int and see if the value is greater than 0. If it is, I add the section header for and objectAtIndex:0, otherwise I use the static one. But when the count gets to 2, for section 2 and objectAtIndex:1, it breaks and throws EXC_BAD_ACCESS.
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
int value = [[self Array1] count];
if(section == 0)
return #"Countries";
if (value > 0) {
if (section == value){
return [[self Array1] objectAtIndex:section - 1];
}
}
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
int count = [[self Array1] count];
return count + 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
int value = [[self Array1] count];
if (section == 0) {
return [self.Array count];
}
if (value > 0) {
if (section == [[self Array1] count]) {
NSString *initialLetter = [[self Array1] objectAtIndex:section - 1];
// get the array of elements that begin with that letter
NSArray *elementsWithInitialLetter = [self elementsWithInitialLetter:initialLetter];
// return the count
return [elementsWithInitialLetter count];
}
}
}
Looks like you're just missing a retain on the iVar backing the Array1 method. declare the array as a property:
#property (nonatomic, retain) NSArray* datastore;
Then cache the value you're referring to in the Array1 method in this method (probably in viewDidLoad).
self.datastore = [self Array1];
Then replace all remaining references to [self Array1] with self.datastore. Build run and see if it still crashes. (Don't forget to set self.datastore = nil in your dealloc!

NSOutlineView indentation issue

I'm using an NSOutlineView object to represent a file structure and am finding that it will not correctly indent any children which are expandable, though it will indent children that aren't.
Here's a picture to show what I mean:
In this example, "AnotherFolder" is a child of "Folder2" yet it does not indent in line with the other indented files. Curiously enough, the child "AnotherFile.java" of "AnotherFolder" does indent correctly (2 levels in).
I have tried setting properties such as "indentationFollowsCells" to no avail. This seems as though it should be very simple but I can't solve it.
Thanks!
Edit: Some extra information upon request:
I am using the NSOutlineViewDataSource protocol for the implementation, here is the code related to that:
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {
return item;
}
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {
NSMutableDictionary* dict;
if(item == nil) {
dict = fileTree;
} else {
dict = [((MyFile*) item) children];
}
NSArray* keys = [dict allKeys];
NSArray* sorted = [keys sortedArrayUsingSelector:#selector(compare:)];
NSString* key = [sorted objectAtIndex:index];
return [dict objectForKey:key];
}
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
return [[item children] count] > 0;
}
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
if(item == nil) {
return [fileTree count];
}
return [[item children] count];
}
Try changing your outline view from a Source outline view to a normal one.
I ran into this right now, and found it a bit strange that nine years after this post, the problem still persists.
This behaviour is baked into the Source style: the first row of the standard content is aligned with the header cell rather than indented, so everything is shifted over by one level.
If you use header cells, you want this behaviour, and everything is fine. If you don't want to use header cells, not using a SourceList is your only option.