Access property inside a block objective c - 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]
});

Related

How to load several photos with assetForURL with a list of URLs

For a list of URLs I need to load the photos with ALAssetsLibrary:assetForURL, and this within one method.
Since this method works async but it is not iterating over the passed list of URLs, as we all know.
I found this snippet (which should work):
- (void)loadImages:(NSArray *)imageUrls loadedImages:(NSArray *)loadedImages callback: (void(^)(NSArray *))callback
{
if (imageUrls == nil || [imageUrls count] == 0) {
callback(loadedImages);
}
else {
NSURL *head = [imageUrls head];
__unsafe_unretained id unretained_self = self;
ALAssetsLibrary* library = [[ALAssetsLibrary alloc] init];
[library assetForURL:head resultBlock:^(ALAsset *asset) {
ALAssetRepresentation *assetRepresentation = asset.defaultRepresentation;
UIImage *image = [UIImage imageWithCGImage:assetRepresentation.fullResolutionImage scale:assetRepresentation.scale orientation:(UIImageOrientation)assetRepresentation.orientation];
[unretained_self loadImages:[imageUrls tail] loadedImages:[loadedImages arrayByAddingObject:image] callback:callback];
} failureBlock:^(NSError *error) {
[unretained_self loadImages:[imageUrls tail] loadedImages:loadedImages callback:callback];
}];
}
}
How do I write the method definition in the form (above all the callback)
void loadImages(NSArray *imageUrls, NSArray *loadedImages, ...) ?
How do I call this method from another method (again mainly the callback part) ?
Can the callback be in the calling method or a 3rd method needed for this? and how does this method need to be written?
I have found the snippet here: http://www.calebmadrigal.com/functional-programming-deal-asynchronicity-objective-c/
Use NSThread to call the loadImages method.
NSMutableArray *imageCollection = [NSThread detachNewThreadSelector:#selector (loadImages:)
toTarget:self
withObject:imageUrlsCollection];
- (NSMutableArray *)loadImages:(NSArray *)imageUrls
{
ALAssetsLibrary* library = [[ALAssetsLibrary alloc] init];
NSMutableArray *loadedImages = [[NSMutableArray alloc] init];
#try
{
for(int index = 0; index < [imageUrls count]; index++)
{
NSURL *url = [imageUrls objectAtIndex:index];
[library assetForURL:url resultBlock:^(ALAsset *asset) {
ALAssetRepresentation *assetRepresentation = asset.defaultRepresentation;
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *image = [UIImage imageWithCGImage:assetRepresentation.fullResolutionImage scale:assetRepresentation.scale orientation:(UIImageOrientation)assetRepresentation.orientation];
[loadedImages addObject:image];
});
} failureBlock:^(NSError *error) {
NSLog(#"Failed to get Image");
}];
}
}
#catch (NSException *exception)
{
NSLog(#"%s\n exception: Name- %# Reason->%#", __PRETTY_FUNCTION__,[exception name],[exception reason]);
}
#finally
{
return loadedImages;
}
}
Note: With ARC,take care about invalid attempt to access ALAssetPrivate past the lifetime of its owning ALAssetsLibrary issue
Here is the fix :)

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).

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

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.

Remove curled corner from qlgenerator thumbnail

How do I remove the curled icon a thumbnail create in a quicklook plugin?
Screenshot of current icon:
Screenshot of what I want:
GeneratePreviewForURL.m:
#include <CoreFoundation/CoreFoundation.h>
#include <CoreServices/CoreServices.h>
#include <QuickLook/QuickLook.h>
#import "GenerateIcon.h"
OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef preview, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options);
void CancelPreviewGeneration(void *thisInterface, QLPreviewRequestRef preview);
/* -----------------------------------------------------------------------------
Generate a preview for file
This function's job is to create preview for designated file
----------------------------------------------------------------------------- */
OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef preview, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options)
{
// To complete your generator please implement the function GeneratePreviewForURL in GeneratePreviewForURL.c
[GenerateIcon generatePreviewWithRef:preview URL:url];
return noErr;
}
void CancelPreviewGeneration(void *thisInterface, QLPreviewRequestRef preview)
{
// Implement only if supported
}
GenerateIcon.m:
//
// GenerateIcon.m
// Windows Binary Icon
//
// Created by Asger Hautop Drewsen on 2/5/12.
// Copyright (c) 2012 Asger Drewsen. All rights reserved.
//
#import "GenerateIcon.h"
#implementation GenerateIcon
+(void) generateThumbnailWithRef:(QLThumbnailRequestRef)requestRef URL:(CFURLRef)url
{
[GenerateIcon generateMultiWithThumbnailRef:requestRef PreviewRef:nil URL:url];
}
+(void) generatePreviewWithRef:(QLPreviewRequestRef)requestRef URL:(CFURLRef)url
{
[GenerateIcon generateMultiWithThumbnailRef:nil PreviewRef:requestRef URL:url];
}
+(void) generateMultiWithThumbnailRef:(QLThumbnailRequestRef)thumbnail PreviewRef:(QLPreviewRequestRef)preview URL:(CFURLRef)url
{
#autoreleasepool {
NSString * tempDir = NSTemporaryDirectory();
if (tempDir == nil)
tempDir = #"/tmp";
NSFileManager *fileManager = [[NSFileManager alloc] init];
NSString *directory = [tempDir stringByAppendingFormat: [NSString stringWithFormat:#"%#-%.0f", #"exe-icons", [NSDate timeIntervalSinceReferenceDate] * 1000.0]];
//NSString *directory = [tempDir stringByAppendingPathComponent:#"com.tyilo.exe-icons"];
/*for (NSString *file in [fileManager contentsOfDirectoryAtPath:directory error:nil])
{
[fileManager removeItemAtPath:file error:nil];
}*/
[fileManager createDirectoryAtPath:directory withIntermediateDirectories:YES attributes:nil error:nil];
[[NSTask launchedTaskWithLaunchPath:#"/usr/local/bin/wrestool" arguments:[NSArray arrayWithObjects:
#"-t",
#"group_icon",
#"-o",
directory,
#"-x",
[(__bridge NSURL *)url path],
nil]] waitUntilExit];
NSArray *icons = [fileManager contentsOfDirectoryAtPath:directory error:nil];
if (icons.count > 0)
{
NSImage *image = [[NSImage alloc] initWithContentsOfFile:[directory stringByAppendingPathComponent: [icons objectAtIndex:0]]];
NSData *thumbnailData = [image TIFFRepresentation];
CGSize size = image.size;
NSDictionary *properties = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:size.width],kQLPreviewPropertyWidthKey,
[NSNumber numberWithInt:size.height],kQLPreviewPropertyHeightKey,
nil];
CGContextRef CGContext;
if (thumbnail)
{
CGContext = QLThumbnailRequestCreateContext(thumbnail, size, TRUE, (__bridge CFDictionaryRef)properties);
}
else
{
CGContext = QLPreviewRequestCreateContext(preview, size, TRUE, (__bridge CFDictionaryRef)properties);
}
if(CGContext) {
NSGraphicsContext* context = [NSGraphicsContext graphicsContextWithGraphicsPort:(void *)CGContext flipped:size.width > size.height];
if(context) {
//These two lines of code are just good safe programming…
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:context];
NSBitmapImageRep *thumbnailBitmap = [NSBitmapImageRep imageRepWithData:thumbnailData];
[thumbnailBitmap draw];
//This line sets the context back to what it was when we're done
[NSGraphicsContext restoreGraphicsState];
}
// When we are done with our drawing code QLThumbnailRequestFlushContext() is called to flush the context
if (thumbnail)
{
QLThumbnailRequestFlushContext(thumbnail, CGContext);
}
else
{
QLPreviewRequestFlushContext(preview, CGContext);
}
// Release the CGContext
CFRelease(CGContext);
}
/*NSLog(#"%#", [directory stringByAppendingPathComponent: [icons objectAtIndex:0]]);
CGImageRef image = (__bridge CGImageRef) [[NSImage alloc] initByReferencingFile:[directory stringByAppendingPathComponent: [icons objectAtIndex:0]]];
QLThumbnailRequestSetImage(thumbnail, image, properties);*/
}
else
{
NSLog(#"Failed to generate thumbnail!");
}
}
}
#end
Edit: Added screenshots.
You need to add the undocumented "IconFlavor" key to the properties dictionary that you supply to QLThumbnailRequestCreateContext() or QLThumbnailRequestSetXXX(), and give it the value 1 for minimal decoration.
See here for an example. At the top of that file are some other values I've discovered for "IconFlavour".
The aspect of your icons is automatically chosen by Quick Look and there is no public way to customize that. What is your type conformance tree?
For more information on UTIs see Uniform Type Identifiers Overview. Note that your type conformance tree won't necessarily translate to what you want from Quick Look but at least you will have a sane starting point.

Create an array of UIImages from camera roll

I would like to get all of the images from the camera roll and create an array of UIImages from them.
I have been trying to figure out how to do this for about a day now and I've gotten nowhere. I can't seem to figure out how to retrieve only items from the Camera Roll. It appears that all of the samples that I've seen all enumerate over all of the photo albums. I might be wrong about that though.
Any help would be appreciated. Thanks!
Have you tried ALAssetsLibrary? like this:
assets = [[NSMutableArray array] init]; // Prepare array to have retrieved images by Assets Library.
void (^assetEnumerator)(ALAsset *, NSUInteger, BOOL *) = ^(ALAsset *asset, NSUInteger index, BOOL *stop) {
if(asset != NULL) {
[assets addObject:asset];
dispatch_async(dispatch_get_main_queue(), ^{
[self insertArray];
});
}
};
void (^assetGroupEnumerator)(ALAssetsGroup *, BOOL *) = ^(ALAssetsGroup *group, BOOL *stop) {
if(group != nil) {
[group enumerateAssetsUsingBlock:assetEnumerator];
}
};
// Create instance of the Assets Library.
library = [[ALAssetsLibrary alloc] init];
[library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos // Retrieve the images saved in the Camera roll.
usingBlock:assetGroupEnumerator
failureBlock: ^(NSError *error) {
NSLog(#"Failed.");
}];
that'll nab them. then do this to render them (this is wicked hacky. you'll want to import them as needed and not all at once like this, or you'll run into memory issues and crash)
-(void) insertArray {
int i = assetCount++;
if (i>20) {
return;
}
ALAssetRepresentation *rep = [[assets objectAtIndex:i] defaultRepresentation];
CGImageRef iref = [rep fullResolutionImage];
CGSize cSize = CGSizeMake(75,75);
UIImage *largeimage = [UIImage imageWithCGImage:iref];
UIImage *resizedimage = (UIImage *)[largeimage resizedImage:cSize interpolationQuality:kCGInterpolationHigh];
UIImageView *newView = [[UIImageView alloc] initWithImage:resizedimage];
if((i>0)&&(i%4 == 0)){
rowCount++;
}
colCount = i%4;
newView.frame = CGRectMake(4+(colCount*(75+4)), 4+(rowCount*(75+4)), 75, 75);
[sv addSubview:newView];
[sv setContentSize:CGSizeMake(320, 85+(rowCount*(75+4)))];
NSLog(#"sv frame size is %# and i is %i", NSStringFromCGRect(sv.frame), i);
}