1

I use ABPeoplePickerNavigationController to import iPhone Contacts into my app. I recently decided, instead of just saving the thumbnail image, to save the entire image as well. With iPad and possibly needing larger images with different cropping, I think this is the way to go. However, when saving some new contacts after importing, the save is LONG, depending on the size of the photo. No photo and the save is immediate, so I know it has to do with the size of the photo and what I am doing with it. Relevant code is below in case anyone can point out what I am doing wrong...

    - (BOOL)peoplePickerNavigationController: (ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)contact {

    NSLog(@"%s", __FUNCTION__);
    self.selectedThumbnailImage = nil;
    self.selectedImage = nil;   

    if(ABPersonHasImageData(contact)){

        self.selectedThumbnailImage = nil;
        NSData *imgData = (NSData *)ABPersonCopyImageData(contact); 
        UIImage *pickedImage = [UIImage imageWithData:imgData];
        [imgData release];

        self.selectedImage = pickedImage;

        CGSize imageSize = pickedImage.size;
        CGSize targetSize = CGSizeMake(110.0,120.0);
        CGFloat width = imageSize.width;
        CGFloat height = imageSize.height;
        CGFloat targetWidth = targetSize.width;
        CGFloat targetHeight = targetSize.height;
        CGFloat scaleFactor = 0.0;
        CGFloat scaledWidth = targetWidth;
        CGFloat scaledHeight = targetHeight;
        CGPoint thumbnailPoint = CGPointMake(0.0,0.0);

        if (CGSizeEqualToSize(imageSize, targetSize) == NO) 
        {
            CGFloat widthFactor = targetWidth / width;
            CGFloat heightFactor = targetHeight / height;

            if (widthFactor > heightFactor) 
                scaleFactor = widthFactor; // scale to fit height
            else
                scaleFactor = heightFactor; // scale to fit width
            scaledWidth  = width * scaleFactor;
            scaledHeight = height * scaleFactor;

            // center the image
            if (widthFactor > heightFactor)
            {
                thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5; 
            }
            else 
                if (widthFactor < heightFactor)
                {
                    thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5;
                }
        }       

        UIGraphicsBeginImageContext(targetSize);

        CGRect thumbnailRect = CGRectZero;
        thumbnailRect.origin = thumbnailPoint;
        thumbnailRect.size.width  = scaledWidth;
        thumbnailRect.size.height = scaledHeight;

        [pickedImage drawInRect:thumbnailRect];

        self.selectedThumbnailImage = UIGraphicsGetImageFromCurrentImageContext();
        [[NSNotificationCenter defaultCenter] postNotificationName:@"personChanged" object:nil]; 

        UIGraphicsEndImageContext();
    }

    self.nameFirstString = (NSString *)ABRecordCopyValue(contact, kABPersonFirstNameProperty);
    self.nameLastString = (NSString *)ABRecordCopyValue(contact, kABPersonLastNameProperty);
    [nameFirstTextField setText:nameFirstString];
    [nameLastTextField setText:nameLastString];

    self.person.birthday = (NSDate *)ABRecordCopyValue(contact, kABPersonBirthdayProperty); 

    [self updateRightBarButtonItemState];
    [self dismissModalViewControllerAnimated:YES];

    return NO;
}

- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier{

    NSLog(@"%s", __FUNCTION__);
    return NO;
}

- (void)saveImage {

    // Delete any existing image.
    NSManagedObjectContext *context = [[UIApplication sharedDelegate] managedObjectContext];
    Image *oldImage = person.image;
    if (oldImage != nil) {
        [context deleteObject:(NSManagedObject*)oldImage];
    }

    // Create an image object for the new image.
    UIImage *newImage = [NSEntityDescription insertNewObjectForEntityForName:@"Image" inManagedObjectContext:context];
    [newImage setValue:selectedImage forKey:@"image"];
    [self.person setValue:newImage forKey:@"image"];
}
- (IBAction)save:(id)sender {

    NSLog(@"%s", __FUNCTION__);

    [self.person setValue:selectedThumbnailImage forKey:@"thumbnailImage"];
    [self saveImage];

    self.person.nameFirst = nameFirstString;
    self.person.nameLast = nameLastString;

    NSError *error;
    NSManagedObjectContext *context = [[UIApplication sharedDelegate] managedObjectContext];
    if (![context save:&error]) {
        NSLog(@"AddPersonViewController - addViewControllerDidFinishWithSave - Person MOC save error %@, %@", error, [error userInfo]);
        exit(-1);  // Fail
    }

    [self.delegate addPersonViewController:self didFinishWithSave:YES didEditPerson:person];
}

EDIT: I changed the saveImage code to the following and got the error 'Illegal attempt to establish a relationship 'person' between objects in different contexts':

- (void)saveImage {

// Delete any existing image.
//NSManagedObjectContext *context = [[UIApplication sharedDelegate] managedObjectContext];

NSLog(@"%s", __FUNCTION__);

NSManagedObjectContext *savingPhotoContext = [[NSManagedObjectContext alloc] init];
self.savingPhotoMOC = savingPhotoContext;                                                           
[savingPhotoMOC setPersistentStoreCoordinator:[[[UIApplication sharedDelegate] managedObjectContext] persistentStoreCoordinator]];
[savingPhotoContext release];       

Image *oldImage = person.image;
if (oldImage != nil) {
    [savingPhotoMOC deleteObject:(NSManagedObject*)oldImage];
}

// Create an image object for the new image.
UIImage *newImage = [NSEntityDescription insertNewObjectForEntityForName:@"Image" inManagedObjectContext:savingPhotoMOC];
[newImage setValue:selectedImage forKey:@"image"];
[newImage setValue:self.person forKey:@"person"];
//[self.person setValue:newImage forKey:@"image"];

NSNotificationCenter *dnc = [NSNotificationCenter defaultCenter];
[dnc addObserver:self selector:@selector(addControllerContextDidSave:) name:NSManagedObjectContextDidSaveNotification object:savingPhotoMOC];

NSError *error;
if (![savingPhotoMOC save:&error]) {
    NSLog(@"Occasion View Controller - addViewControllerDidFinishWithSave - Adding MOC save error %@, %@", error, [error userInfo]);
    exit(-1);  // Fail
}
[dnc removeObserver:self name:NSManagedObjectContextDidSaveNotification object:savingPhotoMOC];
self.savingPhotoMOC = nil;

}- (void)addControllerContextDidSave:(NSNotification*)saveNotification {

NSLog(@"%s", __FUNCTION__);
NSManagedObjectContext *context = [[UIApplication sharedDelegate] managedObjectContext];
[context mergeChangesFromContextDidSaveNotification:saveNotification];  

}

SAHM
  • 4,078
  • 7
  • 41
  • 77
  • If it takes long, you should do this the background and either use delegate callback or nsnotification to inform other functions that might be using it when the save is done. – Bushra Shahid Sep 27 '11 at 06:23
  • OK.. I guess that would involve using a separate managedObjectContext? To simplify things I only use one MOC for my entire app.. I'm sure that is a bad idea, but it was getting a little too confusing with all the contexts. I really didn't see a need for multiple contexts but now I think I do. Thanks. – SAHM Sep 27 '11 at 15:19
  • Hmm, okay, showing my inexperience now, but I see that I can't save the new object 'person' in one context while saving the object 'image' (and assigning it to 'person') in another context. I thought this would work if I merged in the new context, but: "Illegal attempt to establish a relationship 'person' between objects in different contexts".. not sure where to go from here.. – SAHM Sep 27 '11 at 15:48
  • Maybe someone could point me to a good tutorial on simple threading? – SAHM Sep 30 '11 at 01:14
  • 1
    you can use GCD or nsoperationqueue...for gcd have a look at 'fiery robot's blog. I like it because he explains things not just give code for copy pasting. – Bushra Shahid Oct 03 '11 at 06:19
  • Looks pretty good, seems like it will take a little while to learn. I wonder if there is a good tutorial for a simple Core Data item add using GCD.. do you know of one? – SAHM Oct 05 '11 at 01:23
  • you can learn it in hrs...thats not a while – Bushra Shahid Oct 05 '11 at 07:15
  • I've gotten to look at it a little more today, and it looks pretty great. It shouldn't take too terribly long to learn. Do you want to make your response an official answer for this question, then I can accept it? – SAHM Oct 06 '11 at 20:36

1 Answers1

1

If it takes long, you should do this the background and either use delegate callback or nsnotification to inform other functions that might be using it when the save is done. you can use GCD or nsoperationqueue...for gcd have a look at 'fiery robot's blog. I like it because he explains things not just give code for copy pasting.

Bushra Shahid
  • 3,579
  • 1
  • 27
  • 37
  • I'm going to set this as the answer because I know it's correct. However, I personally am having a difficult time getting it to work and haven't yet (see [this link](http://stackoverflow.com/questions/7693556/saving-large-image-with-core-data-and-gcd)). Hope to get it working soon. Thanks for the point in the right direction. – SAHM Oct 10 '11 at 21:18