Why aren't variables retained outside Asset-Library block? - objective-c

I've come across a weird issue that I've spent ages working on.
I'm basically trying to get the filepath of a photo out of the asset library and draw it on a pdf using the CGRectMake method using the code below:
In the .h file:
#property (strong, nonatomic) UIImage *pdfSnagImage;
In the .m file:
NSURL *url = [[NSURL alloc] initWithString:pdf.photo];
ALAssetsLibrary* library = [[ALAssetsLibrary alloc] init];
[library assetForURL:url resultBlock:^(ALAsset *asset) {
ALAssetRepresentation *rep = [asset defaultRepresentation];
self.filename = [rep filename];
NSLog(#"filename for image is: %#", self.filename);
CGImageRef iref = [rep fullResolutionImage];
if (iref) {
self.pdfImage = [UIImage imageWithCGImage:iref];
NSLog(#"image height %f", self.pdfImage.size.height);
}
} failureBlock:^(NSError *error) {
NSLog(#"Couldn't load asset %# => %#", error, [error localizedDescription]);
}];
UIImage *testImage = self.pdfImage;
[testImage drawInRect:CGRectMake( (pageSize.width - testImage.size.width/2)/2, 350, testImage.size.width/2, testImage.size.height/2)];
What's happening is that the UIImage after the block, testImage is actually being resolved before the block - therefore it is Null, as self.pdfImage is only set within the block.
If I put those lines of code in the block I get an error with the CGRectMake method, Invalid Context 0x0.
How on earth can I assign the self.pdfImage UIImage first then draw it?

The block is executed asynchronously...it's being executed on a separate thread. This is the design of the assets lib api.

Related

Access property inside a block objective c

- (IBAction)saveBtn:(id)sender {
UIImage* imageToSave = [self imageByCombiningImage:self.backgroundImage.image withImage:self.tempImage.image];
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
// Request to save the image to camera roll
[library writeImageToSavedPhotosAlbum:[imageToSave CGImage] orientation:(ALAssetOrientation)[imageToSave imageOrientation] completionBlock:^(NSURL *assetURL, NSError *error){
if (error) {
NSLog(#"error");
} else {
CGFloat compression = 0.0;
NSData *imageData = UIImageJPEGRepresentation(imageToSave, compression);
UIImage *compressedImage = [[UIImage alloc]initWithData:imageData];
NSMutableString *imageName = [[NSMutableString alloc] initWithCapacity:0];
CFUUIDRef theUUID = CFUUIDCreate(kCFAllocatorDefault);
if (theUUID) {
[imageName appendString:CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, theUUID))];
CFRelease(theUUID);
}
[imageName appendString:#".png"];
NSLog(#"Image name: %#", imageName);
//Image Data to web service
[self uploadImage:UIImageJPEGRepresentation(compressedImage, 1.0) filename:imageName];
_savedImageURL = assetURL;
[library assetForURL:_savedImageURL
resultBlock:resultblock
failureBlock:failureblock];
}];
}
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset)
{
ALAssetRepresentation *rep = [myasset defaultRepresentation];
CGImageRef iref = [rep fullResolutionImage];
if (iref) {
UIImage *largeimage = [UIImage imageWithCGImage:iref];
//image Property needs to be access here
}
};
I cannot access any property inside this block. Like I have made a UIImage property in the .h file but I cannot accessed that in that result block.
If the code you've posted is really the one you're using, then it's defining a variable which references a block. But that variable is not part of your class and you thus cannot reference any instance variables.
Instead, the variable is on the "C top-level", outside any class. You might want to turn it into an instance variable, for example like this:
#interface MyClass : SomeSuperClass
{
// Define an instance variable.
ALAssetsLibraryAssetForURLResultBlock resultblock;
}
#end
#implementation MyClass
- (void)someMethod
{
// Initialize the variable in `init`, `viewDidLoad` or whichever
// method suits you.
resultblock = ^(ALAsset *myasset)
{
ALAssetRepresentation *rep = [myasset defaultRepresentation];
CGImageRef iref = [rep fullResolutionImage];
if (iref) {
UIImage *largeimage = [UIImage imageWithCGImage:iref];
//image Property needs to be access here
}
};
}
#end
You could also use a property instead.
Next solution is you can create block property from whole controller like this:
__block MyViewController *blocksafeSelf = self;
and inside block you can use this property to access:
blocksafeSelf.myProperty;
or call method:
dispatch_async(dispatch_get_main_queue(), ^
[blocksafeSelf processNewMessage:text]
});

NSURL of UIImage from UIImagePickerController returns (null)

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)image editingInfo:(NSDictionary *)editInfo {
userURL = [editInfo objectForKey:UIImagePickerControllerMediaURL];
userImage = image;
userImageView.image=userImage;
[self dismissViewControllerAnimated:YES completion:nil];}
I then take NSURL userURL, and put it in an UIActivityViewController to use to upload the image. However this never works, and always fails as it's trying to upload (null). However, when I use a preset image included in the xcode project and the following code, it always works and uploads correctly:
NSURL *url = [[NSBundle mainBundle] URLForResource:#"kitten.jpg" withExtension:nil];
If it helps, I'm using https://github.com/goosoftware/GSDropboxActivity
When I use UIImagePickerControllerReferenceURL instead of UIImagePickerControllerMediaURL, I get the following error:
[WARNING] DropboxSDK: File does not exist (/asset.JPG)
Failed to upload assets-library://asset/asset.JPG?id=EECAF4D0-A5ED-40E7-8E6F-3A586C0AB06E&ext=JPG
In theory, UIImagePickerControllerMediaURL is only populated for a movie, not an image. If you use the UIImagePickerControllerReferenceURL then the URL that you're given is not a URL for the file system, but rather the assets library. To get the file from that you'll need to use the asset library. Use something like the following :
typedef void (^ALAssetsLibraryAssetForURLResultBlock)(ALAsset *asset);
typedef void (^ALAssetsLibraryAccessFailureBlock)(NSError *error);
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset){
ALAssetRepresentation *rep = [myasset defaultRepresentation];
CGImageRef iref = [rep fullResolutionImage];
if (iref){
UIImage *myImage = [UIImage imageWithCGImage:iref scale:[rep scale] orientation:(UIImageOrientation)[rep orientation]];
// Do whatever you now want with your UIImage
}
};
ALAssetsLibraryAccessFailureBlock failureblock = ^(NSError *myerror){
//failed to get image.
};
ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc] init];
[assetslibrary assetForURL:[filePath objectAtIndex:0] resultBlock:resultblock failureBlock:failureblock];
This takes care of the blocks that will handle your request, but you still need to actually request the resource from the assets library. For that, try this :
ALAssetsLibrary* assetslibrary = [[[ALAssetsLibrary alloc] init] autorelease];
NSURL myAssetUrl = [NSURL URLWithString:[editInfo objectForKey:UIImagePickerControllerMediaURL]];
[assetslibrary assetForURL:myAssetUrl resultBlock:resultblock failureBlock:failureblock];
And in theory, you should get what you're after :)
The code for this was given by Ramshad and further discussion of it can be found here
Hopefully this will help you with what you're after. Sorry if it's a bit too late :(
Edit
Note that if you're not using ARC, you'll need to tidy up the memory in this example as I haven't included any memory management at all.

name of the picked image xcode

I want to get the name of the image picked from the library or newly clicked in -
-(void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
I am using this code:
-(void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
if ([UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeSavedPhotosAlbum])
{
AppDelegate *del = [[UIApplication sharedApplication]delegate];
[self dismissModalViewControllerAnimated:YES];
image = [info objectForKey:UIImagePickerControllerOriginalImage];
NSLog(#"imagekbgfg=====>%#",image);
[self.imageView setImage:image];
imgName = [info objectForKey:UIImagePickerControllerOriginalImage];
del.mainImgName = imgName;
del.imageData = UIImageJPEGRepresentation(image, 1.0);
del.imageApp = image;
}
if ([UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera])
{
image = [info objectForKey:UIImagePickerControllerOriginalImage];
}
}
How can I do this?
Using the code bellow you will get path like:assets-library://asset/asset.JPG?id=79450962-C0FD-484C-808B-83E045F40972&ext=JPG when your picking image from photo album. This path is valid. It contains the asset ID and asset type.
// Firstly get the picked image's file URL.
NSURL *imageFileURL = [info objectForKey:UIImagePickerControllerReferenceURL];
// Then get the file name.
NSString *imageName = [imageFileURL lastPathComponent];
NSLog(#"image name is %#", imageName);
If you want to get image user picked again using the path you get using UIImagePickerController.(Here maybe you want to save the path so that you can read that image back without asking user to pick that image again) You will need the ALAssetsLibrary otherwise you don't need it.
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
__block UIImage *returnValue = nil;
[library assetForURL:imageFileURL resultBlock:^(ALAsset *asset) {
returnValue = [UIImage imageWithCGImage:[[asset defaultRepresentation] fullResolutionImage]];
} failureBlock:^(NSError *error) {
NSLog(#"error : %#", error);
}];
Take a look at the UIImagePickerControllerDelegate Protocol. The string UIImagePickerControllerReferenceURL contains an URL of your file. You could use it to construct the filename from it.
Other than that there seems to be no built-in way to get the file name from an UIImage or UIImageView object.
Reference:
Stackoverflow answer
You can get the file name using below code:
NSURL *assetURL = [imageDic objectForKey:UIImagePickerControllerReferenceURL];
__block NSString *originalFileName = nil;
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library assetForURL:assetURL resultBlock:^(ALAsset *asset){
originalFileName = [[asset defaultRepresentation] filename];
}
originalFileName give the original file name of resource.

Issue on capture screenshot of video

i want to take a screenshot of a video on my ipad app.
I searched on SO, and i found a lot of sample code. I tried everything but nothing seem to work.
I tried all of this methods:
1) Try with : MPMoviePlayerController
- (void) previewWithPlayer:(NSString*)path image:(UIImageView*)imView
{
MPMoviePlayerController *player = [[MPMoviePlayerController alloc] initWithContentURL:[NSURL URLWithString:path]];
UIImage *thumbnail = [player thumbnailImageAtTime:1.0 timeOption:MPMovieTimeOptionNearestKeyFrame];
[player stop];
[player release];
imView.image = thumbnail;
}
2) Try with : AVAssetImageGenerator - v1
- (void) generateImage:(NSString*)path image:(UIImageView*)imView
{
AVAsset *asset = [AVAsset assetWithURL:[NSURL URLWithString:path]];
AVAssetImageGenerator *imageGenerator = [[AVAssetImageGenerator alloc]initWithAsset:asset];
CMTime time = CMTimeMake(1, 1);
UIImage *thumbnail = [UIImage imageWithCGImage:[imageGenerator copyCGImageAtTime:time actualTime:NULL error:NULL]];
imView.image = thumbnail;
}
13) Try with : AVAssetImageGenerator - v2
- (void) generateImage:(NSString*)path image:(UIImageView*)imView
{
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:[NSURL URLWithString:path] options:nil];
AVAssetImageGenerator *generator = [[AVAssetImageGenerator alloc] initWithAsset:asset];
generator.appliesPreferredTrackTransform=TRUE;
[asset release];
CMTime thumbTime = CMTimeMakeWithSeconds(2,30);
AVAssetImageGeneratorCompletionHandler handler = ^(CMTime requestedTime, CGImageRef im, CMTime actualTime, AVAssetImageGeneratorResult result, NSError *error){
if (result != AVAssetImageGeneratorSucceeded) {
NSLog(#"couldn't generate thumbnail, error:%#", error);
}
UIImage *thumbImg = [[UIImage imageWithCGImage:im] retain];
imView.image = thumbImg;
[generator release];
};
CGSize maxSize = CGSizeMake(320, 180);
generator.maximumSize = maxSize;
[generator generateCGImagesAsynchronouslyForTimes:[NSArray arrayWithObject:[NSValue valueWithCMTime:thumbTime]] completionHandler:handler];
}
But nothig works.
I tried with MOV, MP4, nothing.
Path is correct, video is working.
NSString *fPath = [[NSBundle mainBundle] pathForResource:#"VideoA" ofType:#"mp4"];
NSLog(#"%#", fPath);
[self generateImage:fPath image:_ImgA];
What's could be the problem? My image view show nothing and no error are returned.
iOS is 6.0/5.1, on iPad simulator/device.
Video is 854×480 pixels, H.264, AAC. About 30Mb of size.
please help me because i'm going crazy with this issue.
thanks.
edit
on device return this error:
couldn't generate thumbnail, error:Error Domain=NSURLErrorDomain
Code=-1 "unknown error" UserInfo=0x1e0a2f30
{NSUnderlyingError=0x1e0a3900 "The operation couldn’t be completed.
(OSStatus error -12935.)", NSLocalizedDescription=unknown error}
Solved.
The trick: use fileURLWithPath:, not URLWithString:. Apparently the difference is really, really significant.
thanks to Noah. https://stackoverflow.com/a/4201419/88461

Array with UIImageView elements - setImageWithURL

I have such code
arrayWithImages = [[NSMutableArray alloc] init];
NSEnumerator *enumForNames = [arrayWithNames objectEnumerator];
NSEnumerator *enumForURLs = [arrayWithURLs objectEnumerator];
id objName, objURL;
while(objName = [enumForNames nextObject]) {
objURL = [enumForURLs nextObject];
UIImageView *anImage = nil;
[anImage setImageWithURL:[NSURL URLWithString:objURL]];
(...)
[arrayWithImages addObject:anImage];
}
And each time I got SIGABRT in line with "[arrayWithImages addObject:anImage];"
What's here wrong?
I don’t see an setImageWithURL method on UIImageView. Where is this from?
Is there any output from the SIGABRT crash?
Try this code:
// Download the image
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:objURL]];
// Make an UIImage out of it
UIImage *anImage = [UIImage imageWithData:imageData];
// Create an image view with the image
UIImageView *imageView = [[UIImageView alloc] initWithImage:anImage];
// Check to make sure it exists
if (imageView != nil) {
// Add it to your array
[arrayWithImages addObject:imageView];
else {
NSLog(#"Image view is nil");
}
Note that you should download the images asynchronously to avoid hanging the loop. This blog post discussing asynchronous image downloading.
Also if you know that [enumForURLs nextObject]; will return an NSString (or even better, NSURL) you should assign objURL to type NSString (or NSURL).