I am experimenting with the Social Framework.
I was wondering if there is any possible way to attach the last photo taken and currently saved in the camera roll using this implementation from my app:
- (IBAction)shareByActivity:(id)sender {
NSArray *activityItems;
if (self.sharingImage != nil) {
activityItems = #[self.sharingImage, self.sharingText, self.addURL];
} else {
activityItems = #[self.sharingText, self.addURL];
}
UIActivityViewController *activityController =
[[UIActivityViewController alloc] initWithActivityItems:activityItems
applicationActivities:nil];
[self presentViewController:activityController
animated:YES completion:nil];
}
If so how do I modify the shareImage name in this specific portion of my -(void)viewDidLoad ?
self.sharingImage = [UIImage imageNamed:#"notSureWhatToPutHere"];
Everything works well, the social panel opens and has all the needed service. My only request is to find out how to call the latest image from the camera roll.
I am not sure if I need the image real name or if there is any other way to achieve what i would like: a post with a picture attached (most recent picture in camera roll).
Any help is appreciated.
You can pretty painlessly get the last image in the camera roll using the asset library framework. Give this a try:
#import <AssetsLibrary/AssetsLibrary.h>
Then to get the image:
ALAssetsLibrary *cameraRoll = [[ALAssetsLibrary alloc] init];
[cameraRoll enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *images, BOOL *stop) {
[images setAssetsFilter:[ALAssetsFilter allPhotos]];
[images enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndex:[images numberOfAssets] - 1] options:0 usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop){
if (result) {
ALAssetRepresentation *rep = [result defaultRepresentation];
self.sharingImage = [UIImage imageWithCGImage:[rep fullScreenImage]];
}
}];
}
failureBlock: ^(NSError *error) {
NSLog(#"An error occured: %#",error);
}];
Thanks for the great help NSPostWhenIdle!
I was able to resolve the issue of getting "newest photos" from camera roll by moving my ALAssettsLibrary chuck of code away from viewDidLoad and created a refreshAlbum
- (void) refreshAlbum
{
ALAssetsLibrary *cameraRoll = [[ALAssetsLibrary alloc] init];
[cameraRoll enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *images, BOOL *stop) {
[images setAssetsFilter:[ALAssetsFilter allPhotos]];
[images enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndex:[images numberOfAssets] - 1] options:0 usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop)
{
if (result) {
ALAssetRepresentation *rep = [result defaultRepresentation];
self.sharingImage = [UIImage imageWithCGImage:[rep fullScreenImage]];
}
}];
}
failureBlock: ^(NSError *error) {
NSLog(#"An error occured: %#",error);
}];
}
Then I am calling the newly created refreshAlbum from the same button that enable the shareByActivity
- (IBAction)refreshButton:(id)sender {
[self refreshAlbum];
}
This work particularly well as my app does not have any picture to share as it starts up.
The first time that imageShare become available is after activating the shareByActivity button that now carries the refreshButton function as well!
Thanks again, this really work.
Related
Please find the below code for fetching images from Photo library
- (void) initilizeAssetForPhotoLibrary {
if (!assets) {
assets = [[NSMutableArray alloc] init];
} else {
[assets removeAllObjects];
}
ALAssetsGroupEnumerationResultsBlock assetsEnumerationBlock = ^(ALAsset *result, NSUInteger index, BOOL *stop) {
if (result) {
[assets addObject:result];
}
};
ALAssetsFilter *onlyPhotosFilter = [ALAssetsFilter allPhotos];
[assetsGroup setAssetsFilter:onlyPhotosFilter];
[assetsGroup enumerateAssetsUsingBlock:assetsEnumerationBlock];
}
- (NSMutableArray *) getImagesFromPhotoLibrary {
for(int i=0; i<assets.count; i++) {
ALAsset *asset = [assets objectAtIndex:i];
ALAssetRepresentation *assetRepresentation = [asset defaultRepresentation];
UIImage *getImage = [UIImage imageWithCGImage:[assetRepresentation fullScreenImage] scale:[assetRepresentation scale] orientation:(UIImageOrientation)[assetRepresentation orientation]];
[arrImages addObject:getImage];
}
return arrImages;
}
Actually my requirement is that I need to load all image from specific album in application for creating the slideshow.
When I am loading less than 100 images then it works fine but above it gets memory warning and after that it crashed.
Please help me if anyone has different idea to load images and less memory consumption. Thanks in advanced.
Keep all the images in memory will not do, there is just not enough memory for this.
You will need to fill the array with the ALAssetRepresentation of the images and load the image only when you are ready to display it. This way you will only have the image in memory that you are really displaying.
then write down below code and let me know it is working or not!!!
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
(unsigned long)NULL), ^(void) {
for(int i=0; i<assets.count; i++) {
ALAsset *asset = [assets objectAtIndex:i];
ALAssetRepresentation *assetRepresentation = [asset defaultRepresentation];
UIImage *getImage = [UIImage imageWithCGImage:[assetRepresentation fullScreenImage] scale:[assetRepresentation scale] orientation:(UIImageOrientation)[assetRepresentation orientation]];
[arrImages addObject:getImage];
}
return arrImages;
});
Happy Coding...!!!!
When I am fetching photos using the ALAssetLibrary, for some images, the AssetRepresentation.size comes zero, which does not make the image, on my ImageView.
Here is the code:
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library enumerateGroupsWithTypes:ALAssetsGroupAlbum usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
if ([[group valueForProperty:ALAssetsGroupPropertyName] isEqual:self.groupName]) {
[group enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop){
//Get the asset type
NSString *assetType = [result valueForProperty:ALAssetPropertyType];
if ([assetType isEqualToString:ALAssetTypePhoto]) {
NSLog(#"Photo Asset");
}
//Get URLs for the assets
NSDictionary *assetURLs = [result valueForProperty:ALAssetPropertyURLs];
NSUInteger assetCounter = 0;
for (NSString *assetURLKey in assetURLs) {
assetCounter++;
}
//Get the asset's representation object
ALAssetRepresentation *assetRepresentation = [result defaultRepresentation];
//From this asset representation, we take out data for image and show it using imageview.
dispatch_async(dispatch_get_main_queue(), ^(void){
CGImageRef imgRef = [assetRepresentation fullResolutionImage];
//Img Construction
UIImage *image = [[[UIImage alloc] initWithCGImage:imgRef] autorelease];
NSLog(#"before %#:::%lld", [image description], [assetRepresentation size]); //Prints '0' for size
if((image != nil)&& [assetRepresentation size] != 0){
//display in image view
}
else{
// NSLog(#"Failed to load the image.");
}
});
}];
}
}failureBlock:^(NSError *error){
NSLog(#"Error retrieving photos: %#", error);
}];
[library release];
Please Help. What am I doing wrong here? and how should I get the image?
Joe Smith is right! You release the library too soon. The moment the AssetLibrary is released, all the asset object will be gone with it. And Because these enumeration is block codes so the release of AssetLibrary will be executed somewhere during the reading process.
I suggest that you create your assetLibrary in the app delegate to keep it alive and only reset it when you receive change notification ALAssetsLibraryChangedNotification from the ALAssetLibrary
I think you released the assets library too soon.
I have an issue where I see memory usage climbing (but no obvious leaks in Instruments) in my app.
I have a test project with two viewControllers: MainViewController and PhotoViewController. The MainViewController contains a single button that simply pushes PhotoViewController via a uinavigationcontroller pushViewController method.
In PhotoViewController, I am using the ALAssetsLibrary to populate a UITableView with images. I essentially do this in two parts. First, I check to see what assetGroups are available, as I need to show images from the Camera Roll and the Photolibrary. Once that is done, I call another method to enumerate through the actual Assets.
Here is the strange behavior: if I push the PhotoViewController and let it finish the entire enumeration and populate the UITableView, and then pop out back to the MainViewController, everything is fine.
However, if I repeatedly and rapidly push and pop out of the PhotoViewCOntroller (while it hasn't yet finished enumerating and populating the UITableiew),then I see my memory usage gradually climbing until the app finally dies. I don't see any obvious leaks in Instruments.
I don't know the relevant code, but here are two methods that use to enumerate. Of course, in dealloc, I am releasing the relevant ivars.
Is there some way to cancel an enumeration upon pop?
Just as a note, I am basing my test code off this project (https://github.com/elc/ELCImagePickerController), although heavily customized. However, I just tested with that code and the same issue happens. Note that you would only see memory usage climb if you have sufficient ALAssets to enumerate. If there are too few, then it would finish enumerating beforeyou couldpop back out.
Thank you!
- (void)getAssetGroups
{
// Load Albums into assetGroups
dispatch_async(dispatch_get_main_queue(), ^
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Group enumerator Block
void (^assetGroupEnumerator)(ALAssetsGroup *, BOOL *) = ^(ALAssetsGroup *group, BOOL *stop)
{
if (group == nil)
{
// check what data is available
if([savedPhotosGroup numberOfAssets] > 0 && [libraryGroup numberOfAssets] > 0)
{
// User has both Camera Roll and Photo Library albums
self.tableData = [NSMutableDictionary dictionaryWithObjectsAndKeys:
savedPhotoAssets, NSLocalizedString(#"PHOTOPICKER_CAMERAROLL", nil),
libraryPhotosAssets, NSLocalizedString(#"PHOTOPICKER_PHOTOLIBRARY", nil),
nil];
self.sectionKeys = [NSArray arrayWithObjects:NSLocalizedString(#"PHOTOPICKER_CAMERAROLL", nil), NSLocalizedString(#"PHOTOPICKER_PHOTOLIBRARY", nil), nil];
}
else if([libraryGroup numberOfAssets] == 0)
{
// User only has Camera Roll
self.tableData = [NSMutableDictionary dictionaryWithObjectsAndKeys:
savedPhotoAssets, NSLocalizedString(#"PHOTOPICKER_CAMERAROLL", nil),
nil];
self.sectionKeys = [NSArray arrayWithObjects:NSLocalizedString(#"PHOTOPICKER_CAMERAROLL", nil), nil];
}
else
{
//User only has Photo Library
self.tableData = [NSMutableDictionary dictionaryWithObjectsAndKeys:
libraryPhotosAssets, NSLocalizedString(#"PHOTOPICKER_PHOTOLIBRARY", nil),
nil];
self.sectionKeys = [NSArray arrayWithObjects:NSLocalizedString(#"PHOTOPICKER_PHOTOLIBRARY", nil), nil];
}
NSLog(#"Done enumerating groups");
[self performSelectorInBackground:#selector(enumeratePhotos) withObject:nil];
[self.tview performSelector:#selector(reloadData) withObject:nil afterDelay:1];
return;
}
ALAssetsGroupType groupType = [[group valueForProperty:ALAssetsGroupPropertyType] unsignedIntValue];
if(groupType == ALAssetsGroupSavedPhotos)
{
self.savedPhotosGroup = group;
}
else if(groupType == ALAssetsGroupLibrary)
{
self.libraryGroup = group;
}
};
// Group Enumerator Failure Block
void (^assetGroupEnumberatorFailure)(NSError *) = ^(NSError *error) {
NSLog(#"A problem occured %#", [error description]);
};
// Enumerate Albums
[library enumerateGroupsWithTypes: ALAssetsGroupSavedPhotos | ALAssetsGroupLibrary
usingBlock:assetGroupEnumerator
failureBlock:assetGroupEnumberatorFailure];
NSLog(#"Draining pool");
[pool drain];
});
}
-(void)enumeratePhotos {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSLog(#"enumerating photos");
[savedPhotosGroup enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop)
{
if(result == nil)
{
return;
}
CustomAsset *customAsset = [[[CustomAsset alloc] initWithAsset:result] autorelease];
[customAsset setParent:self];
[savedPhotoAssets addObject:customAsset];
}];
[libraryGroup enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop)
{
if(result == nil)
{
return;
}
CustomAsset *customAsset = [[[CustomAsset alloc] initWithAsset:result] autorelease];
[customAsset setParent:self];
[libraryPhotosAssets addObject:customAsset];
}];
NSLog(#"done enumerating photos");
[tview performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
// only do this if I want to re-select some assets
if(assetsToRestore)
{
for(NSDictionary *dict in assetsToRestore)
{
NSIndexPath *indexPathToRestore = [dict objectForKey:#"selectedAssetIndexPath"];
int tagToRestore = [[dict objectForKey:#"selectedAssetTag"] intValue];
[self selectAssetWithIndexPath:indexPathToRestore andIndex:tagToRestore];
}
}
[pool drain]; }
correct me if i'm wrong, I thought using autorelease pools was supposed to be done like this now:
#autoreleasepool {
(statements)
}
That worked for me.
I know how to let user select an image from UIImagePickerController, but I don't want that.
I just want to have NSArray of images stored in the phone, but I don't want to involve user (to select a one and then have that image...),rather, I have created my own custom Image selector controller and want to have source as the gallary.
You can easily do that using the AVFoundation and AssetsLibrary framework. Here is the code to access all the photos:
-(void)addPhoto:(ALAssetRepresentation *)asset
{
//NSLog(#"Adding photo!");
[photos addObject:asset];
}
-(void)loadPhotos
{
photos = [[NSMutableArray alloc] init];
library = [[ALAssetsLibrary alloc] init];
// Enumerate just the photos and videos group by using ALAssetsGroupSavedPhotos.
if([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
{
[library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop)
{
// Within the group enumeration block, filter if necessary
[group setAssetsFilter:[ALAssetsFilter allPhotos]];
[group enumerateAssetsUsingBlock:^(ALAsset *alAsset, NSUInteger index, BOOL *innerStop)
{
// The end of the enumeration is signaled by asset == nil.
if (alAsset)
{
ALAssetRepresentation *representation = [alAsset defaultRepresentation];
[self addPhoto:representation];
}
else
{
NSLog(#"Done! Count = %d", photos.count);
//Do something awesome
}
}];
}
failureBlock: ^(NSError *error) {
// Typically you should handle an error more gracefully than this.
NSLog(#"No groups");
}];
}
}
So, in init function I am getting images via AssetsLibrary
//initWithNibName:
photoArray = [[NSMutableArray alloc ]init];
ALAssetsLibrary *asset = [[ALAssetsLibrary alloc] init];
void (^enumerateGroup)(ALAsset *, NSUInteger, BOOL *) = ^(ALAsset *result, NSUInteger index, BOOL *stop)
{
if (result != nil) {
[photoArray addObject:result];
NSLog(#"%#", result);
}
};
void (^enumerationBlock)(ALAssetsGroup *, BOOL *) = ^(ALAssetsGroup *group, BOOL *stop)
{
if (group != nil) {
[group enumerateAssetsUsingBlock:enumerateGroup];
}
};
[asset enumerateGroupsWithTypes:ALAssetsGroupAll
usingBlock:enumerationBlock
failureBlock:^(NSError *error) {NSLog(#"Something went wrong");}];
[asset release];
//loadView
- (void)loadView
{
UIView *view = [[UIView alloc ] init];
NSLog(#"%d", [photoArray count]);
self.view = view;
[view release];
}
Log from console:
2011-06-24 18:55:12.255 xxx[9450:207] 0 //
2011-06-24 18:55:12.306 xxx[9450:207] ALAsset - Type:Photo, URLs:{
"public.jpeg" = "assets-library://asset/asset.JPG?id=1000000001&ext=JPG";
And I am confused. As you can see in the log, loadView executed code faster than initWithNibName. This is because getting images via AssetLibrary take some time. But I think all of this code is executing in one thread, so loadView should wait, for initWithNibName.
The documentation for -enumerateGroupsWithTypes:... and -enumerateAssetsUsingBlock:... doesn't say that those methods execute synchronously. From what you've found, it looks like they do their enumeration on a different thread so that you don't have to wait.