ios imageview animation using array causes semaphore_wait_trap - ios

I'm trying to animate an imageview by providing it with an array of images. These images have been extracted from a spritesheet having lots of images one after the other.
Here is my code:
-(NSMutableArray*)getIndividualImagesFromImage:(UIImage*)collectionOfEmoticonImages
{
NSMutableArray *imagesArray = [[NSMutableArray alloc] init];
//Let's find out the total number of images in this big image
int totalEmoticonImages = collectionOfEmoticonImages.size.width/WIDTH_HEIGHT_OF_EMOTICONS;
for(int emoticonIndex = 0; emoticonIndex < totalEmoticonImages; emoticonIndex++)
{
//crop the image at each index and put smaller images in the array
UIImage *croppedImage = [[UIImage alloc] init];
croppedImage = [self cropImage:collectionOfEmoticonImages atIndex:emoticonIndex];
[imagesArray addObject:croppedImage];
}
return imagesArray;
}
-(void)getEmoticonImageViewAnimating:(NSInteger)index
{
imagesArray = [self getIndividualImagesFromImage:[emoticonsPNGImages objectAtIndex:index]];
//Animate using the images in imagesArray
emoImageView.animationImages = [NSArray arrayWithArray:imagesArray];
emoImageView.animationDuration = 2;
emoImageView.animationRepeatCount = 1;
[emoImageView startAnimating];
[self performSelector:#selector(animationDidFinish) withObject:nil
afterDelay:emoImageView.animationDuration];
}
Everything works fine if I comment [emoImageView startAnimating]; line. But with this statement, animation works fine for 6 times but then I get a semaphore_wait_trap on trying to run it 7th time.
Any pointers?

Related

NSMutableArray creates issue for images

I have an array with dictionaries in it. Each dictionary contains UIImage & String values.
But when I try to delete object using
[arr removeObjectAtIndex:button.tag];
then it just decreases the count but image (Object) is not deleted.
So, my Images are overlapping when try to delete.
It also creates problem when I try to add objects in the array
[arr insertObject:dict atIndex:0];
so, I used
[_postObj.arr_images addObject:dict]; instead of insertObject
Help me to solve this
Objects use reference counting and both NSMutableArray and UIImageView will retain the UIImage object.
This means that removing it from the array will not automatically remove it from the UIImageView and you must remove it from both explicitly:
[arr removeObjectAtIndex:button.tag];
_imageView.image = nil;
Problem is in your frame setting of UIImageView. Please try using below code (not tested by myself) and see if this fixes the issue for you. Basically, I am adjusting X position of images based on previous image's width.
CGFloat imageXM = 5.0;
CGFloat imageXPos = 0;
for (UIImage *image in arr_images) {
CGRect imageFrame = CGRectMake(imageXPos + imageXM, 0.0, image.size.width, image.size.height);
imageXPos = imageXPos + image.size.width;
UIImageView *imageView = [[UIImageView alloc] initWithFrame:imageFrame];
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.image = image;
[scl addSubview:imageView];
}
As in your code
[picker dismissViewControllerAnimated:YES completion:^{
[arr_images insertObject:chosenImage atIndex:0];
[self scrollImages_Setup2];
}];
[arr_images insertObject:chosenImage atIndex:0]; that means you need only one image to display on scroll view. Then why you take a loop:
-(void)scrollImages_Setup2
{
for (int k=0; k<arr_images.count; k++) {
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake((CGRectGetWidth(scl.frame) * k) + CGRectGetWidth(scl.frame), 0, CGRectGetWidth(scl.frame), CGRectGetHeight(scl.frame))];
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.image=[arr_images objectAtIndex:k] ;
[scl addSubview:imageView];
}
scl.contentSize = CGSizeMake((CGRectGetWidth(scl.frame) * arr_images.count)+CGRectGetWidth(scl.frame), CGRectGetHeight(scl.frame));
}
You just right the code only for display the Image. you don't need to scroll view for displaying only one image. I think you are not clear what you want.
Your problem regarding the overwriting the images because of loop. so remove the loop, when you display a single image. Please remove the scrollview contentSize, because image_array will increase, when you add the multiple images in array and size width will be large. so remove arr_images.count as well.

stop UIimageview animation at last image

NSArray *imageNames = #[#"2_00000.png", #"2_00001.png", #"2_00002.png", #"2_00003.png",
#"2_00004.png", #"2_00005.png", #"2_00006.png", #"2_00007.png",
#"2_00008.png", #"2_00009.png", #"2_00010.png", #"2_00011.png",
#"2_00012.png", #"2_00013.png", #"2_00014.png", #"2_00015.png",
#"2_00016.png", #"2_00017.png", #"2_00018.png", #"2_00019.png",
#"2_00020.png", #"2_00021.png", #"2_00022.png", #"2_00023.png",
#"2_00024.png", #"2_00025.png", #"2_00026.png", #"2_00027.png",
#"2_00028.png", #"2_00029.png", #"2_00030.png", #"2_00031.png",
#"2_00032.png", #"2_00033.png", #"2_00034.png", #"2_00035.png",
#"2_00036.png", #"2_00037.png", #"2_00038.png", #"2_00039.png",
#"2_00040.png", #"2_00041.png", #"2_00042.png", #"2_00043.png",
#"2_00044.png", #"2_00045.png", #"2_00046.png", #"2_00047.png",
#"2_00048.png", #"2_00049.png", #"2_00050.png", #"2_00051.png",
#"2_00052.png", #"2_00053.png", #"2_00054.png", #"2_00055.png",
#"2_00056.png", #"2_00057.png", #"2_00058.png", #"2_00059.png",
#"2_00060.png", #"2_00061.png", #"2_00062.png", #"2_00063.png"];
NSMutableArray *images = [[NSMutableArray alloc] init];
for (int i = 1; i < imageNames.count; i++) {
[images addObject:[UIImage imageNamed:[imageNames objectAtIndex:i]]];
}
// Normal Animation
UIImageView *animationImageView = [[UIImageView alloc] initWithFrame:CGRectMake(-6, 40, 200, 1034)];
animationImageView.animationImages = images;
animationImageView.animationDuration = 2;
animationImageView.animationRepeatCount = 0;
[self.view addSubview:animationImageView];
[animationImageView startAnimating];
If you know the animationRepeatCount before starting your animation
animationImageView.animationRepeatCount = 1 //For 1 loop
animationImageView.image = animationImageView.animationImages.lastObject;
For stoping animation dynamically
animationImageView.animationRepeatCount = 0 //infinite loop
When you need to stop the animation
[animationImageView stopAnimating];
animationImageView.image = animationImageView.animationImages.lastObject;
first set the animationRepeatCount to 1 and after starting the animation create a block that fires after the animation duration in this block set the animationImageView image to the last image .
dispatch_time_t dispatchAfter = dispatch_time(DISPATCH_TIME_NOW, animationImageView.animationDuration * NSEC_PER_SEC);
dispatch_after(dispatchAfter, dispatch_get_main_queue(), ^(void){
animationImageView.image = [images lastObject];
});
You could use transitionWithView:duration:options:animation:completion: of UIView for animating your UIImageView.
- (void)animatePictureTransition:(NSMutableArray *)arrayImages{
[UIView transitionWithView:imageViewButton
duration:1.0
options:UIViewAnimationOptionTransitionFlipFromRight
animations:^{
UIImage *newImage = (UIImage *)arrayImages.firstObject;
imageViewButton.image = newImage;
} completion:^(BOOL finished){
[arrayImages removeObjectAtIndex:0];
if(arrayImages.count) {
[self animatePicturesTransition:arrayImages];
}
}];
}
You could use this recursive, and call the method again inside the completion blog and pass through the remaining UIImages this way there shouldn't be a problem to stop at the last UIImages.
I would also recommend you, to load the UIImages dynamically from the resource folder in this way:
- (NSMutableArray *)getImagesWithSuffix:(NSString *)start andEndSuffix:(NSString *)end{
NSMutableArray *imageArray = [[NSMutableArray alloc] init];
for (int i = 1; i < 100; i++) {
NSString *fileName = [NSString stringWithFormat:#"%#%d%#.png", start, i, end];
if([self fileExistsInProject:fileName]){
[imageArray addObject:[UIImage imageNamed:fileName]];
} else {
break;
}
}
return imageArray;
}
Always writing all names of your UIImages takes a lot of time and can cause any problems. This way you just have to order your pictures so you can loop over.
To use this code, create a category of NSMutableArray and you can use it!
problem is with this line..
animationImageView.animationRepeatCount = 0;
by setting animationRepeatCount = 0 its set to infinite by default.
if u want it stop at last image you can go with
animationImageView.animationRepeatCount = 1; // specify no of times u need to run your animation.

How to recognize particular image is animating during imageview animation?

I am creating a timer which is used to play a song for 6 seconds.But i want that whenever i clicked the button it will recognized the particular image which is animating at that particular instant.For that i used CALayer but it is not giving the image name.
This is my code:
-(void)playSong
{
timerButton.imageView.animationImages =[NSArray arrayWithObjects:[UIImage imageNamed:#"Timer6.png"],[UIImage imageNamed:#"Timer5.png"],
[UIImage imageNamed:#"Timer4.png"],[UIImage imageNamed:#"Timer3.png"],
[UIImage imageNamed:#"Timer2.png"],[UIImage imageNamed:#"Timer1.png"],
nil];
timerButton.imageView.animationDuration=6.0;
[self.player play];
[timerButton.imageView startAnimating];
}
- (void)handleLongPressGestures:(UILongPressGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateBegan)
{
CALayer *player1 = timerButton.imageView.layer;
}
}
Please help me.
Unfortunately, you cannot get the image name from a UIImageView or UIImage object.
You could, however, set an accessibility identifier to a UIImage object that, in your case, could be it's file name.
Then simply doing someUIImageObject.accessibilityIdentifier should return the file name.
(refer answer)
Example:
UIImage *image = [UIImage imageNamed:#"someImage.png"];
[image setAccessibilityIdentifier:#"someImage"];
NSLog(#"%#",image.accessibilityIdentifier);
Now... you'd expect timerButton.imageView.image.accessibilityIdentifier to give you the image name but that's not how it works when the UIImageView is animating.
This time, we need to access it via the UIImageViews animationImages array property.
For this, we will need some custom logic to get the UIImage object from this animationImages array.
We'll first record the time we started animating the images and with some basic maths, we can calculate the index of the animationImages array that is currently being displayed in the UIImageView.
(refer answer)
Answer (code):
-(void)playSong
{
NSMutableArray *arrImages = [[NSMutableArray alloc] init];
for(int i = 6 ; i > 0 ; i--) {
//Generate image file names: "Timer6.png", "Timer5.png", ..., "Timer1.png"
NSString *strImageName = [NSString stringWithFormat:#"Timer%d.png",i];
//create image object
UIImage *image = [UIImage imageNamed:strImageName];
//remember image object's filename
[image setAccessibilityIdentifier:strImageName];
[arrImages addObject:image];
}
[timerButton.imageView setAnimationImages:arrImages];
[timerButton.imageView setAnimationDuration:6.0f];
[timerButton.imageView setAnimationRepeatCount:0];
[self.player play];
[timerButton.imageView startAnimating];
//record start time
startDate = [NSDate date]; //declare "NSDate *startDate;" globally
}
- (void)handleLongPressGestures:(UILongPressGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateBegan) {
[self checkCurrentImage];
}
}
-(void)checkCurrentImage
{
//logic to determine which image is being displayed from the animationImages array
NSTimeInterval durationElapsed = -1 * [startDate timeIntervalSinceNow];
NSTimeInterval durationPerFrame = timerButton.imageView.animationDuration / timerButton.imageView.animationImages.count;
int currentIndex = (int)(durationElapsed / durationPerFrame) % timerButton.imageView.animationImages.count;
UIImage *imageCurrent = timerButton.imageView.animationImages[currentIndex];
NSString *strCurrentImage = imageCurrent.accessibilityIdentifier;
NSLog(#"%#",strCurrentImage);
}

Retrieve the name of the UIImage at a specific point in a UIScrollView

I'm having trouble getting my head around this; I've looked around for answers on here but either nothing directly applies to my question or I just can't make sense of it. (I am relatively new to this, so apologise if there is an obvious answer.)
I am inserting an array of UIImages (contained within a UIImageView) into a UIScrollView. I can programmatically scroll to points in the ScrollView, but I need to be able to identify by name which image is currently being shown after scrolling (so I can compare the image to one in another ScrollView).
How I have created my arrays and added the images to the ImageView and ScrollView is below.
ViewController.m
-(void)viewDidLoad {
...
// Store the names as strings
stringArr = [[NSMutableArray arrayWithObjects:
#"img0",
#"img1",
#"img2",
#"img3",
nil] retain];
// Add images to array
dataArr = [[NSMutableArray arrayWithObjects:
[UIImage imageNamed:[stringArr objectAtIndex:0]],
[UIImage imageNamed:[stringArr objectAtIndex:1]],
[UIImage imageNamed:[stringArr objectAtIndex:2]],
[UIImage imageNamed:[stringArr objectAtIndex:3]],
nil] retain];
// Use a dictionary to try and make it possible to retrieve an image by name
dataDictionary = [NSMutableDictionary dictionaryWithObjects:dataArr forKeys:stringArr];
i = 0;
currentY = 0.0f;
// Set up contents of scrollview
// I'm adding each of the four images four times, in a random order
for (imageCount = 0; imageCount < 4; imageCount++) {
// Add images from the array to image views inside the scroll view.
for (UIImage *image in reelDictionary)
{
int rand = arc4random_uniform(4);
UIImage *images = [dataArr objectAtIndex:rand];
imgView = [[UIImageView alloc] initWithImage:images];
imgView.contentMode = UIViewContentModeScaleAspectFit;
imgView.clipsToBounds = YES;
// I have tried to use this to tag each individual image
imgView.tag = i;
i++;
CGRect rect = imgView.frame;
rect.origin.y = currentY;
imgView.frame = rect;
currentY += imgView.frame.size.height;
[scrollReel1 addSubview:reel1_imgView];
[reel1_imgView release];
}
}
scrollReel.contentSize = CGSizeMake(100, currentY);
[self.view addSubview:scrollReel];
...
}
This is how I am working out where I am in the ScrollView (currentOffset), and also exactly which image I need to retrieve (symbolNo). The value of symbolNo is correct when I test it, but I am unsure how to use the value with respect to image name retrieval.
NSInteger currentOffset = scrollReel.contentOffset.y;
NSInteger symbolNo = (currentOffset / 100) + 1;
Thanks in advance for any assistance.
There is no way to do this. The UIImage object doesn't store its name once it's loaded.
You could get around this by using the tag property on the image views if all your images have numerical names.
Otherwise you'll need to find a new way to model your data.
You basically need the reverse mapping of what you had. Here is a quick and dirty solution
NSMutableDictionary *indexToImageMap = [NSMutableDictionary new];
for (imageCount = 0; imageCount < 4; imageCount++) {
// Add images from the array to image views inside the scroll view.
for (UIImage *image in reelDictionary)
{
int rand = arc4random_uniform(4);
UIImage *images = [dataArr objectAtIndex:rand];
imgView = [[UIImageView alloc] initWithImage:images];
imgView.contentMode = UIViewContentModeScaleAspectFit;
imgView.clipsToBounds = YES;
// I have tried to use this to tag each individual image
imgView.tag = i;
i++;
[indexToImageMap setObject:imgView forKey:[NSNumber numberWithInt:i];
CGRect rect = imgView.frame;
rect.origin.y = currentY;
imgView.frame = rect;
currentY += imgView.frame.size.height;
[scrollReel1 addSubview:reel1_imgView];
[reel1_imgView release];
}
}
And to look it up you do
NSInteger currentOffset = scrollReel.contentOffset.y;
NSInteger symbolNo = (currentOffset / 100) + 1;
NSImage *image = [indexToImageMap objectForKey:[NSNumber numberWithInt:symbolNo]];
Subclass image view and add imageName property. if i understand what you are asking this should work.
#import <UIKit/UIKit.h>
#interface myImageView : UIImageView
{
__strong NSString *imageName;
}
#property (strong) NSString *imageName;
#end
#import "myImageView.h"
#implementation myImageView
#synthesize imageName;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
#end
then use a dictionary to keep everything instead of array + dictionary.
myImageView *imgView1 = [[myImageView alloc] init];
[imgView1 setImageName:#"image_name_here"];
[imgView1 setImage:[UIImage imageNamed:#"image_name_here"]];
NSMutableDictionary *dicti = [[NSMutableDictionary alloc] init];
[dicti setObject:imgView1 forKey:#"image_name_here_1"];
[dicti setObject:imgView2 forKey:#"image_name_here_2"];
[dicti setObject:imgView... forKey:#"image_name_here_..."];
when you find the imageView you can search image in dictionary. because you know name of the imageView now.

UIImage Flip Image Direction

For a certain UIImageView animationImages, I wish to create 2 arrays - one per each direction.
I have png files I can load into one of the directions, and I wish to flip each image and add it to second direction array.
I tried to do something like this:
_movingLeftImages = [NSMutableArray array];
_movingRightImages = [NSMutableArray array];
int imagesCounter = 0;
while (imagesCounter <= 8)
{
NSString* imageName = [NSString stringWithFormat:#"movingImg-%i", imagesCounter];
UIImage* moveLeftImage = [UIImage imageNamed:imageName];
[_movingLeftImages addObject:moveLeftImage];
UIImage* moveRightImage = [UIImage imageWithCGImage:moveLeftImage.CGImage scale:moveLeftImage.scale orientation:UIImageOrientationUpMirrored];
[_movingRightImages addObject:moveRightImage];
imagesCounter++;
}
Upon the relevant event, I load the UIImageView animationImages with the relevant array; like this:
if ( /*should move LEFT event*/ )
{
movingImageView.animationImages = [NSArray arrayWithArray:_movingLeftImages];
}
if ( /*should move RIGHT event*/ )
{
movingImageView.animationImages = [NSArray arrayWithArray:_movingRightImages];
}
The images are not flipped. They remain as they where.
Any idea how to handle this?
use this code for animating images
UIImageView * animatedImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 200,200)];
NSArray *imageNames = #[#"bird1.png", #"bird2.png"];
animTime =.0;
[animatedImageView setAnimationImages:imageNames] ;
animatedImageView.animationDuration = animTime;
animatedImageView.animationRepeatCount = 1;
[self.view addSubview: animatedImageView];
[animatedImageView startAnimating];

Resources