IOS - Add subview to certain UICollectionView Cells only - iphone

This is the flow of my app: I have UICollectionview with thumbnails of images. User selects images and taps on Save button. Images get saved to camera roll.
This is what I want to do: With a subview I want to mark those cells that user has already tapped on and saved. How do I achieve that?
This is what I have already done....my approach: Obviously I'm missing something and I can't get it resolved.
When user taps on save button I successfully save the IDs of those images to local store using core data. Then, I fetch objects from that entity and put those objects in Array. Now, I'm trying to compare image IDs from fetched array with image IDs in collection view so I can only add subview to those cells where ID's match and that's where i get stuck. I don't know if that's the right approach.
My foolish attempt/s:
This method gets called when user taps on Save Button but it doesn't work properly. I feel as it's randomly adding subviews.
-(void)markSavedImages
{
BOOL isMarked;
NSArray *savedImages = [self fetchSavedPhotos];
NSEnumerator *enumerator = [savedImages objectEnumerator];
NSString *savedImage;
while (savedImage = [enumerator nextObject]) {
if ([[self.eventPhotos valueForKey:#"id"] containsObject:savedImage]) {
isMarked = YES;
}
else
{
isMarked = NO;
}
}
CVCell *cell = [[CVCell alloc]init];
for (NSIndexPath *indexPath in self.livePhotosCollectionView.indexPathsForVisibleItems) {
if (isMarked) {
cell = (CVCell*)[self.livePhotosCollectionView cellForItemAtIndexPath:indexPath];
UIImageView *markedImage = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"markedCVCell.png"]];
[cell addSubview:markedImage];
[cell bringSubviewToFront:markedImage];
cell.userInteractionEnabled = NO;
}
}
}
The self.eventPhotos is an array that contains image's IDs and paths to server.
Please help me guys, I've been killing my self with this and couldn't find anything on google.
EDIT - as per request
-(NSArray*)fetchSavedPhotos
{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication]delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Photos" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"eventID ==[c] %#", _eventID];
[fetchRequest setPredicate:predicate];
NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
NSLog(#"No rows returned");
}
NSArray *savedImages = [fetchedObjects valueForKey:#"imageID"];
return savedImages;
}
Some more information....if I output savedImages array in markSavedImages method i do get the array I want to get. Something like this: (1324,2353,2324). Basically array of image IDs.

To begin with, don't add views just to some cells. This usually results in the view being shown on reused cells and multiple copies of the view being added to the cells which looks confusing and is inefficient.
Instead, add the view to every cell. Do it in your cell subclass and make the view publicly visible or add a method to show and hide it. Then do just that. Every time you return a cell, set the visibility of your subview. This ensures that the cell is always configured correctly and that your configuration code is simple.
For disabling the cells use a similar approach, always choose of the cell is enabled or not and set the user interaction enabled on the cell as a whole.
For your data you have a list of the source image ids and a list of the saved image ids. When you configure the cell you know the associated image id. To check if the cell should be tagged and disabled you can do:
BOOL saved = [self.savedImages containsObject:imageId];
(Compare the current id to the list of selected ids, don't do any looping).

Related

Core Data Persistent Store Coordinator NSURL error

I've been working on an app that takes user ratings about mood. I am using Core Data to store this data. Initially, I am trying to store ratings and strings of "accomplishments". I've set up an entity in Core Data called "Day" with attributes date (of type Date), dailyRating (of type Int16), dailyAccomp (of type String), and dailyAccompRating (of type Int16). My app crashes within my app delegate's Core Data persistentStoreCoordinator method at the following stmnt:
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"SelfEsteemBldr.sqlite"];
The error that I'm given is
[NSPathStore2 URLByAppendingPathComponent:]: unrecognized selector sent to instance 0x4d6f440.
Maybe a little background about how the error comes about might be helpful.
My Main window has a tab bar controller as the rootViewController. Within the tab for the CD model (LogViewController), I've set up a tableView Controller within a navigation controller. The nav bar has an add button, that pushes a new view that basically has textfields so the user can enter the relevant data. Within that view, there is a nav bar with a Done button. When the user is done, the Done button changes to a Save button. When I tap the Save button, the app crashes. The Save button is a UIButtonItem within ViewDidLoad. Here's the code for the save button:
UIBarButtonItem *newSaveButton =
[[UIBarButtonItem alloc]
initWithTitle:NSLocalizedString(#"Save", nil)
style:UIBarButtonItemStylePlain
target:self
action:#selector(performAddNewDay:)];
self.saveButton = newSaveButton;
[newSaveButton release];
The performAddNewDay method within the UIButtonItem looks like this:
- (void) performAddNewDay:(id)paramSender{
SelfEsteemBldrAppDelegate *delegate = (SelfEsteemBldrAppDelegate *)
[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = delegate.managedObjectContext;
NSLog(#"Past init point in PerformAddNewDay");
// Get the values from the text fields
NSInteger dailyRatingAsInteger = [self.textFieldDailyRating.text integerValue];
NSNumber *ddailyRating = [NSNumber numberWithInteger:dailyRatingAsInteger];
NSLog(#"Daily Rating Entered is %#", ddailyRating);
NSString *ddailyAccomplishment = self.textFieldAccomplishment.text;
NSLog(#"Daily Accomplishment Entered is %#", ddailyAccomplishment);
NSInteger dailyAccompRatingAsInteger = [self.textFieldAccomplishmentRating.text integerValue];
NSNumber *ddailyAccompRating = [NSNumber numberWithInteger:dailyAccompRatingAsInteger];
NSLog(#"Daily Accomp Rating Entered is %#", ddailyAccompRating);
// Create a new instance of Day
Day *newDay = [NSEntityDescription
insertNewObjectForEntityForName:#"Day"
inManagedObjectContext:context];
if (newDay != nil){
// Set the properties according to the values we retrieved from the text fields
newDay.dailyAccomp = ddailyAccomplishment;
newDay.dailyRating = ddailyRating;
newDay.dailyAccompRating = ddailyAccompRating;
NSError *savingError = nil;
// Save the new day
if ([context save:&savingError] == YES){
// If successful, simply go back to the previous screen
[self.navigationController popViewControllerAnimated:YES];
} else {
// If we failed to save, display a message
[self
displayAlertWithTitle:NSLocalizedString(#"Saving", nil)
message:NSLocalizedString(#"Failed to save the context", nil)];
}
} else {
// We could not insert a new Day managed object
[self
displayAlertWithTitle:NSLocalizedString(#"New Day", nil)
message:NSLocalizedString(#"Failed To Insert A New Day", nil)];
}
}
I've commented out most of the code to try to find the offending statement, and it seems to be
**NSManagedObjectContext *context = delegate.managedObjectContext;**
That is, if I comment everything below and including this stmnt, app doesn't crash. It doesn't do anything, it just "waits" (as expected). If I uncomment this stmnt, app crashes. SelfEsteemBldrAppDelegate is also imported using the #import "SelfEsteemBldrAppDelegate.h' stmnt.
Again, the error that I'm getting is in the Core Data Stack persistentStoreCoordinator method within SelfEsteemBldrAppDelegate.m. The crash occurs at the following:
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"SelfEsteemBldr.sqlite"];
The error that I'm given is
[NSPathStore2 URLByAppendingPathComponent:]: unrecognized selector sent to instance 0x4d6f440
So, after all that, any ideas as to why I might be getting this message, and what I can do to resolve it? From what I understand, I'm not supposed to interact with the Core Data Stack methods, so I don't want to fool around with that code. Any help would be greatly appreciated. Also, if I've left out any info you may need, please let me know. Thanks.
Normally, I faced this problem and encountered that this error comes when managedObjectContext is nil... To solve this i have used below code and worked fine for me..
if (managedObjectContext == nil) {
managedObjectContext = [(iBountyHunterAppDelegate *) [[UIApplication sharedApplication] delegate] managedObjectContext];
}

NSManagedObjectContext doesn't refresh correctly

hi :) I have a similarly issue like in Working with the same NSManagedObjectContext in multiple tabs
background:
My managedObjectContext (further MOC) is initialised in my appDelegate class and passed throught to multiple tabs by
myViewController.managedObjectContext = self.managedObjectContext; or in the init method with self.managedObjectContext = pContext;
the flow is: the first view is a simple list of collections. The collections are fetched with a NSFetchedResultsController (myViewController : UITableViewController<NSFetchedResultsControllerDelegate>). By selecting one, you navigate deeper, but still passing this MOC.
In the next controller (detailsViewController) I list up some items of this collection what I can interact with (set switches for instance).
I also have an editingObjectContext:
// DetailsViewController.m
NSManagedObjectContext* editingContext = [[NSManagedObjectContext alloc] init];
[editingContext setPersistentStoreCoordinator:[managedObjectContext persistentStoreCoordinator]];
self.editingObjectContext = editingContext;
Now my issue: because my view has to rotate, I am using the folowing trick:
// DetailsViewController.m
DetailsView *localAct = [[DetailsView alloc] initWithManagedObjectContext:managedObjectContext ... ]
DetailsView *localSen = [[DetailsView alloc] initWithManagedObjectContext:managedObjectContext ... ]
UITableView *localContainerView = [[UITableView alloc] init];
self.containerView = localContainerView;
[localContainerView release];
//[...]
[containerView addSubview:actuatorView];
self.tableView = containerView;
further I have a button to manage this items (which of them shall be shown and which not). This button just reloads the table with a new fetchResult.
// DetailsView.m
- (void) manageItems{
managing = !managing;
[viewController setIsManaging:managing]; // parent
self.fetchedResultsController = nil;
NSError *error = nil;
[[self fetchedResultsController] performFetch:&error];
[self reloadData];
[self updateBarButton];
}
The method for putting the items into the context looks so:
// DetailsViewController.m
(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// init + create predicate
NSSet* set = [sen filteredSetUsingPredicate:predicate];
if( [set count] > 0 )
{
for( Act* act in set )
{
[editingObjectContext deleteObject:act];
}
}
else
{
Act* act = [NSEntityDescription insertNewObjectForEntityForName:#"Act" inManagedObjectContext:editingObjectContext];
// do things
}
NSError *error = nil;
[[detailView fetchedResultsController] performFetch:&error];
[self.containerView reloadData];
[detailView reloadData];
}
but after I selected the items in the managed view and clicked save (manageItems), the view doesn't show them :/ i have to switch the tab or to navigate in an other controller (parent or deeper) to actualize it.
my ViewWillAppear method:
// DetailsViewController.m
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
DetailsView *detailView = se ? senView : actView;
// [do uninteresting stuff]
[detailView.fetchedResultsController performFetch:nil];
[self.tableView reloadData];
// [do uninteresting stuff]
}
and viewWillDisapper calls
- (void)saveChanges
{
if( ![editingObjectContext hasChanges] )
return;
// send save-command to server
}
In an earliert Verison where there was only 1 view it worked and I haven't changed realy much... :/ so I don't understand why the MOC is acting like it does. The "manageItems" part is nearly equal, its just a level deeper in the new version (in the DetailsView instead of the controller) ...
if someone can tell me what I can try (always saving to server when switch between managing and normal isn't a solution because the delay in the response from the server is to high for the refresh, so I have the less to flip the view. Also refreshing the views with self.tableView / detailView / self.containerView refresh brings the same result :/ ).
and a second issue: I can't call the "editingObjectContext save:" method after sending to server, because it's throwing errors and don't save at all to local database.
Error in handleChangeResponse:
Error Domain=NSCocoaErrorDomain Code=133020 "The operation couldn’t be completed. (Cocoa error 133020.)" UserInfo=0x4d8bb90 {conflictList=(
"NSMergeConflict (0x5a2fac0) for NSManagedObject (0x5a46a80) with objectID '0x5a46420 ' with oldVersion = 7 and newVersion = 8 and old object snapshot = {\n iconName = noicon;\n [...] ;\n} and new cached row = {\n iconName = noicon;\n [...] \n}"
)}
if you have questions or need some more code (i.e. of the older version) then just ask ;)
thanks in anticipation :)
It seems like I have the solution! Since IOS 5.0 there is a new method for NSManagedObjectContext :
[managedObjectContext setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
Found on http://pauloliveira.net/tech/core-data-merging-conflicts
Setting this attribute to the top-level MOC (in my case in the appDelegate) and no-where else! clears my merging problems ;)
I found the reason why it doesn't worked... forget everything what I wrote above... the problem was in the fetchrequest - concretely: in the predicate... in the earlier versions I used
[NSComparisonPredicate predicateWithLeftExpression: ...]
in the actualy version I use
NSString * predicateFormat = [NSString stringWithFormat: ...];
NSPredicate* predicate = [NSPredicate predicateWithFormat:predicateFormat];
because I had to extend the number of options and also edited the request itself because it made problems in the predicate (comparing a complete object (of the MOC class, extracted from the database) with an entity didn't worked, so I managed the workaround in the DetailsViewController and haven't rolled back my updates in this place :/).
Never thought to waste so much time on this problem >.< but okay, as long as it's resolved :D
I will check if the second issue (with the saving problem) still exists. If not, I will update my post, otherwise this topic isn't closed :/
This may be due to manageobject context in use of object where u'r getting this. Remove all NSManagebobject at the time when you either log out or move back. say end using app. Seems like this...
[NSManagebobjectcontext setManagedObjectsDictionary:[NSMutableDictionary dictionary]];

MPMediaItemCollection Delete selected que/collection?

I am currently looking at Apple's AddMusic example and playing around with it before I start rewriting it into my application.
I noticed that it makes its own little playlist of the songs qued. I want to use the swipe action on the table view to remove songs that a use clicked by mistake.
I have implemented the swipe action but can't work out a way to delete that specific row?
Any idea would be great, below is the code to add it. I tried doing the reverse with no luck. If it's not possible how should I go about it?
Cheers
MainViewController *mainViewController = (MainViewController *) self.delegate;
MPMediaItemCollection *currentQueue = mainViewController.userMediaItemCollection;
MPMediaItem *anItem = (MPMediaItem *)[currentQueue.items objectAtIndex: row];
An MPMediaItemCollection is immutable, ie. you can't change the items. You need to create a new one with all items less the one you want to remove. See below:
NSArray* items = [currentQueue items];
NSMutableArray* array = [NSMutableArray arrayWithCapacity:[items count]];
[array addObjectsFromArray:items];
[array removeObjectAtIndex:row];
MPMediaItemCollection* newCollection = [MPMediaItemCollection collectionWithItems:array];
Be careful to not create an empty collection. It's not allowed and the MPMediaItemCollection will raise an exception.

getting user specific data from a webservice to populate a UITableView

I am trying to populate a UITableView with data for a specific user (his projects) from a webservice, after the user as logged into the app.
for example:
tableData = [[NSMutableDictionary alloc] initWithCapacity:DICTIONARY_INITIAL_CAPACITY];
//CURRENT PROJECT
//get the information from the server
NSArray *currentProject = [[IHMObjectFinderServices sharedIHMObjectFinderServices] getCurrentProjectAsArray:connectedUserId];
if(![currentProject isEqual:[NSNull null]]) {
//put the info in a dictionnary
NSMutableDictionary *currentProjectDictionary = [[NSMutableDictionary alloc] initWithCapacity:PROJECT_DICTIONARY_CAPACITY];
[currentProjectDictionary setObject:[currentProject objectAtIndex:INDEX_PROJECT_NAME] forKey:KEY_PROJECT_NAME];
[currentProjectDictionary setObject:[currentProject objectAtIndex:INDEX_PROJECT_MANAGER] forKey:KEY_PROJECT_MANAGER];
[currentProjectDictionary setObject:[currentProject objectAtIndex:INDEX_PROJECT_TAG] forKey:KEY_PROJECT_TAG];
NSArray *activitiesForProject = [NSArray arrayWithArray:[[IHMObjectFinderServices sharedIHMObjectFinderServices] getAvailableActivities:[currentProjectDictionary valueForKey:KEY_PROJECT_TAG]]];
[currentProjectDictionary setObject:activitiesForProject forKey:KEY_PROJECT_ACTIVITIES];
[tableData setObject:currentProjectDictionary forKey:KEY_CURRENT_PROJECT];
At the moment, my table loads in viewDidLoad method, so the problem is that the user is not yet connected when the table is constructed.
I have read about the reloadData method but I am not sure how to proceed to load the table only once, once the user has logged in. Could someone explain to me what is the correct procedure for this ?
Thanks for your help,
Michael
There should be some kind of call back method when the data for the tableView is available. Just put your reloadData in it.
- (void)dataIsAvailable
{
// ... your code to set the tableData ...
[self.tableView reloadData];
}

Class variable type gets changed

So in my view controller, I run code to populate an NSArray of Customer (custom class) objects. This custom class has objects that are of ANOTHER custom class called Address (a customer has a billing address and a shipping address). In the view controller when a customer in the list is selected, it passes a new view controller a customer object, like so:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
InfoViewController *customerinfoViewController = [[InfoViewController alloc] initWithStyle:UITableViewStyleGrouped andCustomer:[[[customers objectAtIndex:indexPath.section] objectAtIndex:indexPath.row] retain]];
[self.navigationController pushViewController:customerinfoViewController animated:YES];
[customerinfoViewController release];
}
The first time I visit this view controller while running the application, it works fine. However, when I revisit the view controller, something interesting happens. The application crashes, with unrecognized selector sent to instance 0x00whatever. Using the mouseover debugging feature in xCode, I am finding that the first object of the customer's shipAddress variable has its type changed from NSString to NSIndexPath. This does not happen to the customer's billAddress object. Anyone have any idea what is going on here? It seems like I may be having memory management issues but I would definitely like a confirmation on this before I tear my code apart tracking down all the retains and releases....
EDIT: More information here. with the following code, I have an NSMutableArray at the class level. At each iteration of the loop, I am looping through nodes in XML (which works fine). Every time a new letter is detected as the first letter of the name, I create a new subarray and add the customer to it, thus filling my class-level NSMutableArray (customers) with subArrays of customers for each letter of the alphabet detected. My question is about the retains and releases of the cycling customer object. Clang Static says there is an over-retaining error on the customer, but when I fix it according to Clang, the loop crashes. what gives? Related code below:
DDXMLDocument *rootDoc = [[[DDXMLDocument alloc] initWithData:xmlData options:0 error:nil] autorelease];
NSArray *elems = [rootDoc nodesForXPath:#"QBXML/QBXMLMsgsRs/CustomerQueryRs/CustomerRet" error:nil];
DDXMLNode *node;
sectionTitles = [[[NSMutableArray alloc] initWithCapacity:1] retain]; // Letters for UITableView section titles
NSMutableArray *subArray;
NSString *lastchar = #"A";
NSString *testchar;
int indexCount = -1;
customers = [[[NSMutableArray alloc] initWithCapacity:[elems count]] retain];
Customer *newCust;
for (int i = 0; i < [elems count]; i++) {
node = [elems objectAtIndex:i];
newCust = [[Customer alloc] initWithCustomerRetNode:node];
testchar = [[newCust fullName] substringToIndex:1];
if (i == 0 || ![[testchar uppercaseString] isEqualToString:lastchar]) {
[sectionTitles addObject:testchar];
lastchar = testchar;
indexCount++;
subArray = [[NSMutableArray alloc] initWithCapacity:1];
[customers addObject:subArray];
[subArray release];
[[customers lastObject] addObject:[newCust retain]];
}
else {
[[customers lastObject] addObject:[newCust retain]];
}
[newCust release];
}
NOTE: this code works for the most part, but clang doesn't like it.
EDIT: Addresses in the Customer class are assigned like so (which now does not work after Clang fixes)
...
else if ([tempname isEqualToString:#"BillAddress"])
billAddress = [billAddress initWithAddressNode:tempnode];
else if ([tempname isEqualToString:#"ShipAddress"])
shipAddress = [shipAddress initWithAddressNode:tempnode];
...
It sounds like you are having a over release issue, so yes memory management, you might be overreleasing that array you are storing your objects in.Cant really tell from the snippet of code though. Youll have to go and look through the code and find the source. Also using Clang Static Analyzer might be of help to you.