Update image of UIImageView in NSmutableArray? - objective-c

I have:
Class Piece inherit UIImageView;
- (void)setJumpAt:(int)frame {
NSMutableArray *ret = [SkinConstants BallSelected];
NSString *name = [NSString stringWithFormat:#"balls-%d-%d", color - 1, [[ret objectAtIndex:frame] intValue]];
UIImage *a = [UIImage imageNamed:name];
NSLog(#"%d setJumpAt: %#", self.tag ,name);
[self performSelectorOnMainThread:#selector(setImage:) withObject:a waitUntilDone:NO];
[self setNeedsDisplay];
[self setNeedsLayout];}
Class Player contain NSMutableArray of Piece;
Class JumpThread contain NSTimer use to set image of Piece;
- (void) timer_Tick{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
[piece setJumpAt:frame++];
[piece setNeedsDisplay];
if (frame == len)
frame = 0;
dispatch_async(dispatch_get_main_queue(), ^{
});
});}
I run code normal, but image of Piece not change in mainview,
Sorry, I'm not so good at English.

UIImageView can animate images with an array of UIImages and suitable animationDuration
//self is an UIImageView instance
NSArray images = [...]; // your frames
self.animationImages = images;
self.animationDuration = 2.0f // 2.0s
[self startAnimating];

Related

How to get dynamicaly height of image in dispatch_async method in scrollview

I am using below code to get the images from server. i want to get dynamicaly height of image and add image in scrollview.
From below code when i get the height outside the dispatch_async method it shows zero.
How i can get the dynamically height of image with async image load.
- (void)viewDidLoad {
[self LoadViewPublicEvents];
}
-(void) LoadViewPublicEvents
{
for (int i=0;i<arrayPublicEvents.count;i++)
{
UIImageView *img_vw1=[[UIImageView alloc] init];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:#"http://abc.us/uploads/event/%#",[[arrayPublicEvents objectAtIndex:i] valueForKey:#"image"]]]];
UIImage *images = [[UIImage alloc]initWithData:imageData];
dispatch_async(dispatch_get_main_queue(), ^{
img_vw1.image = images;
scaledHeight = images.size.height;
});
});
NSLog(#"%f",scaledHeight); // it print zero
img_vw1.backgroundColor=[UIColor clearColor];
img_vw1.frame=CGRectMake(0,y+5,screen_width,197);
[img_vw1 setContentMode:UIViewContentModeScaleAspectFit];
img_vw1.backgroundColor=[UIColor clearColor];
[self.scrll_vw addSubview:img_vw1];
}
}
Thanks in advance
Your code:
NSLog(#"%f",scaledHeight); // it print zero
img_vw1.backgroundColor=[UIColor clearColor];
img_vw1.frame=CGRectMake(0,y+5,screen_width,197);
[img_vw1 setContentMode:UIViewContentModeScaleAspectFit];
img_vw1.backgroundColor=[UIColor clearColor];
[self.scrll_vw addSubview:img_vw1];
Is being executed, before you loaded the image.
Thus you have to either wait (herefore you could use semaphores until the thread has finished) OR you place it inside of your block.
As you want to modify the UI it makes sense to place it into the main block:
UIImageView *img_vw1=[[UIImageView alloc] init];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:#"http://abc.us/uploads/event/%#",[[arrayPublicEvents objectAtIndex:i] valueForKey:#"image"]]]];
UIImage *images = [[UIImage alloc]initWithData:imageData];
dispatch_async(dispatch_get_main_queue(), ^{
img_vw1.image = images;
scaledHeight = images.size.height;
NSLog(#"%f",scaledHeight); // it print zero
img_vw1.backgroundColor=[UIColor clearColor];
img_vw1.frame=CGRectMake(0,y+5,screen_width,197);
[img_vw1 setContentMode:UIViewContentModeScaleAspectFit];
img_vw1.backgroundColor=[UIColor clearColor];
[self.scrll_vw addSubview:img_vw1];
});
});
For more information, here is a link to Apple's documentation: https://developer.apple.com/library/content/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html

Objective-C: Uploading too many images memory pressure causing app to quit

I am using QBImagePicker to allow multiple image upload. It works fine for up to 25 images being downloaded, but more than that, and the app will quit do to memory pressure while uploading. I would like to allow infinite image upload, and am uncertain how to do so where memory would not be an issue (i.e. perhaps clearing memory after each save). Here is my method to save images (which is called from a loop within the main QBImagePickerController method to save all the selected images):
- (void) saveTheImage:(UIImage *)image fileName:(NSString *)name width:(CGFloat) width height:(CGFloat) height quality:(CGFloat) quality extension:(int)fileNumberExtension
{
UIImage *resizedImage = [self resizeImage:image width:width height:height]; //this is a simple method I have to resize the image sent from the picker
NSData *data = UIImageJPEGRepresentation(resizedImage, quality); //save as a jpeg
NSString *fileName = [NSString stringWithFormat:#"%#%d", name, fileNumberExtension]; //set the filename
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0]; //will be saved in documents
NSString *tempPath = [documentsDirectory stringByAppendingPathComponent:fileName]; //with the filename given
//create a block operation to save
NSBlockOperation* saveOp = [NSBlockOperation blockOperationWithBlock: ^{
[data writeToFile:tempPath atomically:YES];
}];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:saveOp];
}
Thanks in advance!
EDIT
My method to resize the image:
- (UIImage *) resizeImage:(UIImage *)image width:(CGFloat) width height:(CGFloat) height
{
UIImage *resizedImage;
CGSize size = CGSizeMake(width, height);
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0f);
[image drawInRect:CGRectMake(0, 0, width, height)];
resizedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return resizedImage;
}
EDIT 2
Additional methods:
- (void) imagePickerController:(QBImagePickerController *)imagePickerController didSelectAssets:(NSArray *)assets
{
for (int i=0;i<assets.count;i++)
{
ALAssetRepresentation *rep = [[assets objectAtIndex:i] defaultRepresentation];
CGImageRef iref = [rep fullResolutionImage];
UIImage *pickedImage = [UIImage imageWithCGImage:iref scale:[rep scale] orientation:(UIImageOrientation)[rep orientation]];
int fileNumberExtension = [self getHighestImageNumber] + 1; //new images all have a higher file name
//set the ratio (width of image is 294)
CGFloat ratio = pickedImage.size.width / 294;
CGFloat newHeight = pickedImage.size.height / ratio;
if (newHeight < 430) //image is too wide
{
[self saveTheImage:pickedImage fileName:#"img" width:294 height:newHeight quality:0.8f extension:fileNumberExtension];
}
else //if the image is too narrow
{
//set the ratio (height of image is 430)
CGFloat ratio = pickedImage.size.height / 430;
CGFloat newWidth = pickedImage.size.width / ratio;
[self saveTheImage:pickedImage fileName:#"img" width:newWidth height:430 quality:0.8f extension:fileNumberExtension];
}
[self saveTheImage:pickedImage fileName:#"thm" width:78 height:78 quality:0.0f extension:fileNumberExtension]; //save the thumbnail
}
[self dismissImagePickerController];
}
- (void)dismissImagePickerController
{
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void) addImageClicked
{
QBImagePickerController *imagePickerController = [[QBImagePickerController alloc] init];
imagePickerController.delegate = self;
imagePickerController.allowsMultipleSelection = YES;
imagePickerController.maximumNumberOfSelection = 20; //allow up to 20 photos at once
imagePickerController.filterType = QBImagePickerControllerFilterTypePhotos;
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:imagePickerController];
[self presentViewController:navigationController animated:YES completion:nil];
}
Solved this issue by adding by using #autoreleasepool around my for loop in this method:
- (void) imagePickerController:(QBImagePickerController *)imagePickerController didSelectAssets:(NSArray *)assets
This thread was very useful.
You have a memory leak. Leaks usually don't happen because ARC takes care of it for you. (every time you finish using an image, it gets cleared from memory). However, NOT ALL objects are governed by ARC. There are some object types (like CGColorSpaceRef, etc.) that need to be freed manually.
You can check this by running Static Analysis in Xcode. In the top menu bar, select Product -> Analyze. If there are places where you need to free your objects, it will tell you.
To free an object, do:
CGColorSpaceRelease(ref); //where ref is a CGColorSpaceRef.
CGImageRelease(iref); //where iref is a CGImageRef.
or the corresponding method that pertains to your object.

access NSMutableDictionary property in block

I want to save the UIImages into a NSMutableDictionary which is a property in the method of a block,
__block UIImage *headImage = [[UIImage alloc] init];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
headImage = [self getImageFromURL:status.user.profile_image_url];
dispatch_async(dispatch_get_main_queue(), ^{
[headImageView setImage:headImage];
[[self contentView] addSubview:headImageView];
});
});
When debugging the getImageFromURL I found every time the iconDict is empty, and the method returns before setObject:forKey.
-(UIImage *) getImageFromURL:(NSString *)imageUrl {
UIImage * image = [[UIImage alloc] init];
if(iconDict)
{
image = [_iconDict objectForKey:imageUrl];
if (image) {
return image;
}
} else {
_iconDict = [NSMutableDictionary dictionaryWithCapacity:3];
}
image = [self loadImage:imageUrl];
[_iconDict setObject:image forKey:imageUrl];
return image;
}
What's worry about this?
I would say you need to change the semantics of your code. There is no reason to store a UIImage instance in the calling function, so just make it so that getImageFromURL: simply returns boolean success status. If that is YES then get the images from _iconDict:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
if ([self getImageFromURL:status.user.profile_image_url]) {
dispatch_async(dispatch_get_main_queue(), ^{
[headImageView setImage:_iconDict[status.user.profile_image_url]];
[[self contentView] addSubview:headImageView];
});
}
});
...
- (BOOL)getImageFromURL:(NSString *)imageUrl {
UIImage *image = [_iconDict objectForKey:imageUrl];
if (!image) {
image = [self loadImage:imageUrl];
if (image) {
if (!_iconDict)
_iconDict = [NSMutableDictionary dictionaryWithCapacity:3];
_iconDict[imageUrl] = image;
}
}
return image != nil;
}

Lazy loading of PhotoLibrary Images

i found an issue with Photo Library Images. It not displaying first time in my View,Image View is blank while loading first time.
Because i found Asset Library block working on another thread.After reloading my View ,I can see all the Images. However first time the Image Views are Blank.
can any one tell me a good way to deal with the problem
It working with Bundle Images.
also some times console shows that
app is crashing due to Program received signal: “0”. Data Formatters temporarily unavailable, will re-try after a 'continue'. (Unknown error loading shared library "/Developer/usr/lib/libXcodeDebuggerSupport.dylib")
My Code:
for (int j = 0; j<9; j++)
{
//allocating View
UIView *smallView = [[UIView alloc] initWithFrame:CGRectMake(xCordImage, yCordImage, 200, 190)];
// allocating ImageView
imageViewTopic = [[[UIImageView alloc] init] autorelease];
typedef void (^ALAssetsLibraryAssetForURLResultBlock)(ALAsset *asset);
typedef void (^ALAssetsLibraryAccessFailureBlock)(NSError *error);
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset)
{
ALAssetRepresentation *rep = [myasset defaultRepresentation];
CGImageRef iref = [rep fullResolutionImage];
UIImage *images;
if (iref) {
images = [UIImage imageWithCGImage:iref];
}
else {
images = [UIImage imageNamed:#"Nofile.png"];
}
imageViewTopic .image = images ;
};
ALAssetsLibraryAccessFailureBlock failureblock = ^(NSError *myerror)
{
imageViewTopic .image = [UIImage imageNamed:#"Nofile.png"];
NSLog(#"booya, cant get image - %#",[myerror localizedDescription]);
};
NSString *string ;
MyClass *obj = [imageFileNameArray objectAtIndex:j];
**//obj.fileName contains ALAsset URL of a Image**
string = obj.fileName;
NSURL *asseturl = [NSURL URLWithString:string];
ALAssetsLibrary* assetslibrary = [[[ALAssetsLibrary alloc] init] autorelease];
[assetslibrary assetForURL:asseturl resultBlock:resultblock
failureBlock:failureblock];
imageViewTopic.userInteractionEnabled = YES;
imageViewTopic.frame = CGRectMake(0,0, 200, 150);
[currentView addSubview:scroller];
**// adding the imageView to View**
[smallView addSubview:imageViewTopic];
[myView addSubview:smallView];
[scroller addSubview:myView];
}
I am using this method to show images in scroll view with lazy loading. It works well.
First initialize the value of j1. And data is the image data coming from loop from an array.
dispatch_async(dispatch_get_global_queue(0,0), ^{
NSData * data = [[NSData alloc] initWithContentsOfURL:url];
if ( data == nil )
return;
dispatch_async(dispatch_get_main_queue(), ^{
__block int j1=_j;
// WARNING: is the cell still using the same data by this point??
// NSURL *url = [NSURL URLWithString: imageName];
UIImage *image = [UIImage imageWithData: data]; //image.size.height
image1=[[UIImageView alloc] initWithFrame:CGRectMake(j1,10,image.size.width,image.size.height)];
image1.image=image;
CALayer *layer = [image1 layer];
[layer setMasksToBounds:YES];
[layer setCornerRadius:0.0]; //note that when radius is 0, the border is a rectangle
[layer setBorderWidth:3.0];
[layer setBorderColor:[[UIColor whiteColor] CGColor]];
[portfolio_scroll addSubview:image1];
});
});
_j = _j+ 320;

How to parametrize UIView animation?

I have lot of such case statements which would fire various UIView animations. I would like to write a module and parametrize these so I don't have to repeat similar code over and over. What is the best way to pass (parametrized) values for animationRepeatCount & animationDuration?
case 10: //Animation Number 10
//.........
imageAnimation2.animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"monkey_dancing_01.png"],
[UIImage imageNamed:#"monkey_dancing_02.png"],
[UIImage imageNamed:#"monkey_dancing_03.png"],
[UIImage imageNamed:#"monkey_dancing_04.png"],
nil];
imageAnimation2.animationRepeatCount = 4;
imageAnimation2.animationDuration = 2;
[imageAnimation2 startAnimating];
[self loadSoundEffectAudio:#"Monkey_10"];
[self performSelector:#selector(stopSoundEffectAudio) withObject:nil afterDelay:4];
break;
Depending on how regular your assets and animations are, you could make some utility methods to handle much of that.
Like say:
+ (void) loadAnimation:(UIImageView *)imageView name:(NSString *)filename
{
NSMutableArray * array = [[NSMutableArray alloc] init];
UIImage * image;
int index = 1;
NSString * string;
do
{
string = [NSString stringWithFormat:#"%#%04d.png", filename, index++];
image = [UIImage imageNamed:string];
if(image)
[array addObject:image];
} while (image != nil);
imageView.animationImages = array;
imageView.animationDuration = [array count] / 18;
[array release];
}
The above code loads up animations into an array with very regular naming conventions (XXXX.png). They are also all based on the same framerate so I can build the duration at the same time.