NSMutableArray creates issue for images - ios

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.

Related

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.

Using a String as Images Name

I am working on a while loop that spits out 30 images, each image having a different name. The way I approached this was by creating a NSString variable call img, which will be different with each iteration, i.e., "Badguy 1", "Badguy 2", ect... Then using that string as the name of the image being created.
TotalShips = 1
while(TotalShips < 31){
img = [NSString stringWithFormat:#"Badguy %i",TotalShips];
UIImageView *img = [[UIImageView alloc] initWithFrame: CGRectMake(10,20,20,20)];
img.image = [UIImage imageNamed:#"Badguy.png"];
[self.view addSubview: img];
TotalShips = TotalShips + 1;
}
This doesn't seem to work, and I haven't found very much help on changing an image's name.
Thanks,
Alex
The UIImageView has an property animatedImages (NSArray). Vou can easily play auround with animationDuration and animationReapCount. You can start the animation with [myImageView startAnimation] and stop with [myImageView stopAnimation].
Please read: https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIImageView_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40006889-CH3-SW3
The file names can you build in a loop with 'NSString *fileName = [NSString stringWithFormat:#"image%i.png",imageName, iterator]'.
Not sure what you're trying to do, but I'll try to explain what your code does, maybe it helps you understand the problem:
TotalShips = 1
while(TotalShips < 31){
// here your img variable is a string, which will be 'Badguy 1', 'Badguy 2', etc
// you're not using this img value set here anywhere else in your code
img = [NSString stringWithFormat:#"Badguy %i",TotalShips];
// here you redeclare your img as an UIImageView
UIImageView *img = [[UIImageView alloc] initWithFrame: CGRectMake(10,20,20,20)];
// here you assign the image in the imageView to the image 'Badguy.png'
img.image = [UIImage imageNamed:#"Badguy.png"];
[self.view addSubview: img];
TotalShips = TotalShips + 1;
}
So, your code creates 30 different UIImageViews , all of them with the same image: 'Badguy.png', and adds them one on top of the other to the current view. I suppose this is not what you wanted to do.

Delay loading images in UIImageView

I'm trying to loop through some images in a single UIImageView when I tap a button. The image must disappear 0.1 seconds after the button is pressed.
Here's the code:
int tapCount = 0;
UIImage *image0 = [UIImage imageNamed:#"0.jpg"];
UIImage *image1 = [UIImage imageNamed:#"1.jpg"];
UIImage *image2 = [UIImage imageNamed:#"2.jpg"];
imagesArray = [[NSArray alloc] initWithObjects:image0, image1, image2, nil];
-(IBAction)backgroundButton:(id)sender{
self.myImageView.image = [imagesArray objectAtIndex:tapCount%3];
tapCount++;
[self performSelector:#selector(eraseImage) withObject:self afterDelay:0.1];
}
-(void)eraseImage{
self.myImageView.image = nil;
}
The problem is that the images don't appear until I've completed one entire loop (at the 4th tap).
I'm guessing that somehow I must initialize the images in the UIImageView because it takes some time between the tapping and the image appearing, and since it disappears after 0.1 seconds...it doesn't show at all.
I've tried loading them inside viewDidLoad like this:
for(int i = 0; i<[imagesArray count]; i++){
self.myImageView.image = [imagesArray objectAtIndex : i];
}
But it only works with the last image that loads (image2 in this case).
Should I loop between different UIImageView instead of looping through different UIImage inside a single UIImageView?
Any other hints?
Creating a UIImage doesn't actually load the image data (you need to render it to a context for that to happen). So, if your images are large then you could be hiding them before they are actually rendered to the screen. You won't be able to hold many images in memory at the same time, but you can force the image data to be loaded by creating a context and drawing the image into it (which can be done in the background, using CGContextDrawImage).
There are a few 3rd party bits of code which do this, like this or check this discussion.
Use the animationImages and animationDuration property of the UIImageView
https://developer.apple.com/library/ios/documentation/uikit/reference/UIImageView_Class/Reference/Reference.html#//apple_ref/occ/instp/UIImageView/animationImages
I think there is a much simpler way to achieve that animation you are going for. Try the following code:
-(IBAction)backgroundButton:(id)sender{
[UIView animateWithDuration:0.2
delay:nil
options:UIViewAnimationCurveEaseIn
animations:^{
self.myImageView.image = [imagesArray objectAtIndex:tapCount%3];
self.myImageView.image = nil;
}
completion:nil
];
tapCount++;
if (tapCount == 2) {
tapCount = 0;
}
}
I finally managed to work this around using this solution:
First I preload all the images in the background thread
-(void)preload:(UIImage *)image{
CGImageRef ref = image.CGImage;
size_t width = CGImageGetWidth(ref);
size_t height = CGImageGetHeight(ref);
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, width * 4, space, kCGBitmapAlphaInfoMask & kCGImageAlphaPremultipliedFirst);
CGColorSpaceRelease(space);
CGContextDrawImage(context, CGRectMake(0, 0, width, height), ref);
CGContextRelease(context);
}
Then I execute the same action I had in the beginning:
-(IBAction)backgroundButton:(id)sender{
self.myImageView.image = [imagesArray objectAtIndex:tapCount%3];
tapCount++;
[self performSelector:#selector(eraseImage) withObject:self afterDelay:0.1];
}
-(void)eraseImage{
self.myImageView.image = nil;
}

I think I get a deadlock but not quite understand why

I am trying to place an image one after another using GCD like following
-(void)setUpImages {
NSArray *images = #[[UIImage imageNamed:#"blogger-icon.png"],
[UIImage imageNamed:#"gplus-icon.png"],
[UIImage imageNamed:#"facebok-icon.png"]
];
[images enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
dispatch_sync(dispatch_get_main_queue(), ^{
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(80, idx * ((UIImage*)obj).size.height + idx*30 + 10, ((UIImage*)obj).size.width, ((UIImage*)obj).size.height)];
NSLog(#"index is %#",NSStringFromCGRect(imageView.frame));
[imageView setImage:(UIImage*)obj];
[self.view.layer addSublayer:imageView.layer];
sleep(1);
});
}];
}
I am using dispatch_sync because I want it will wait until its block is done ( first image is placed on the screen) then second images will be and so the third one does. And all thing are happening on the main thread now.
However, it seems I am getting a deadlock in the middle and my logic is wrong at some points.
I need help to understand this situation. Please help.
From the documentation of dispatch_sync:
Calling this function and targeting the current queue results in deadlock.
That said, I think you have a design problem and you can achieve the desired result without involving GCD.
Since it looks like you want to update the UI every k seconds, avoid using sleep(1) and invoke a method recursively with a delay using performSelector:withObject:afterDelay:.
Something like
- (void)updateImageViewWithImageAtIndex:(NSNumber *)i {
UIImage * image = self.images[i.intValue];
UIImageView * imageView = [[UIImageView alloc] initWithFrame:CGRectMake(80, idx * image.size.height + idx*30 + 10, image.size.width, image.size.height)];
NSLog(#"index is %#",NSStringFromCGRect(imageView.frame));
[imageView setImage:image];
[self.view.layer addSublayer:imageView.layer];
if (i < images.count - 1) {
[self performSelector:#selector(updateImageViewWithImageAtIndex:) withObject:#(i.intValue++) afterDelay:1];
}
}
- (void)setUpImages {
// Assuming you have declared images as a property
self.images = #[[UIImage imageNamed:#"blogger-icon.png"],
[UIImage imageNamed:#"gplus-icon.png"],
[UIImage imageNamed:#"facebok-icon.png"]
];
[self updateImageViewFromImages:images index:0];
}
Now I may be completely wrong here, but it looks like all you're trying to do is change the image in an image view once a second, in which case your approach is incorrect. UIImageView has methods for this built in. For example:
NSArray *images = #[[UIImage imageNamed:#"blogger-icon.png"],
[UIImage imageNamed:#"gplus-icon.png"],
[UIImage imageNamed:#"facebok-icon.png"]
];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(80, idx * ((UIImage*)obj).size.height + idx*30 + 10, ((UIImage*)obj).size.width, ((UIImage*)obj).size.height)];
[imageView setAnimationDuration:3];
[imageView setAnimationImages:images];
[imageView startAnimating];
I think Gabriele nailed the deadlock above. Don't call dispatch_sync and pass in the main queue.
Another possible problem: I suspect that manipulating a layer's views is not thread-safe. It shouldn't cause a deadlock, but it could cause undesirable consequences.

UIImageView's start animation does not animate flipped images

I have a set of images facing one side(Man facing right side), and I'm flipping it using this method and then adding it into a uiimageview's animationImages. However after adding in the flipped images, and then start to animate, the animation is still facing the right side.
manRunAnimations = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 64, 64)];
NSArray *manAniRunArray = #[[UIImage imageNamed:#"robot1"],
[UIImage imageNamed:#"robot2"],
[UIImage imageNamed:#"robot3"],
[UIImage imageNamed:#"robot4"],
[UIImage imageNamed:#"robot5"],
[UIImage imageNamed:#"robot6"],
];
manRunAnimationsf = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 64, 64)];
NSMutableArray *flippedRunAnimationImages = [[NSMutableArray alloc] init];
for( UIImage *image in manAniRunArray)
{
UIImage * flippedImage = [UIImage imageWithCGImage:image.CGImage scale:image.scale orientation:UIImageOrientationRightMirrored];
[flippedRunAnimationImages addObject:flippedImage];
}
manRunAnimationsf.animationImages = (NSArray *)flippedRunAnimationImages;
testImgView.image = [manRunAnimationsf.animationImages objectAtIndex:0];
manRunAnimationsf.animationDuration = runAnimationSpeedSlider.value;
[manRunAnimationsf startAnimating];
I've even tested it using
testImgView.image = [manRunAnimationsf.animationImages objectAtIndex:5];
This would display one of the flipped images properly on screen just before I do a
[manRunAnimationsf startAnimating];
Once it starts, the animations are not flipped at all!!
Anyone know why?
I can't believe no one knows why, but I found way around it. Is to add the following before I startAnimating:
manRunAnimationsf.transform = CGAffineTransformMake(-1,0,0,1,0,0);
I didn't even need to do the CGImage flips in that for loop.
But the manual image flip should have worked with the imageWithCGImage method, and I really want to know why!! You guys disappoint me. :p

Resources