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.
Related
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.
I'm creating some kind of image gallery. User can swipe between photo's. (Its just the concept, i know there are good library's out there that can handle this, but it's just to explain the concept).
I'm loading in 3 UIImageView, and i'm constantly reusing them. (Lazy loading). Problem is, when i change the image dynamiccaly from one imageview, and keep changing it, the memory is filling up. It's like it does not releases previous attached images. I'm using an array of UIImages that I already downloaded from some kind of web service.
As i load my view, i'm allocing and init my UIImageView, then I add it to the view.
UIImageView* _imgView;
NSArray* _imgArray;
So this array contains UIImage
-(void)changeImageIndex: (int) i
{
_imgView.image = [_imgArray objectAtIndex:i];
}
if i keep changing the imageindex, memory just gets filled up and filled up.. :s.
My project is ARC enabled.
Somebody has a clue how to solve it?
The image is added (actually changed) by this method:
-(void)setCurrentMovie:(GFilm*)film
{
_currentMovie = nil;
_currentMovie = film;
_posterView.image = nil;
[_youtubeView loadHTMLString:#"" baseURL:nil];
[_youtubeView stopLoading];
[_youtubeView setDelegate:nil];
[[NSURLCache sharedURLCache] removeAllCachedResponses];
[_scrollView setContentOffset:CGPointMake(0, 0) animated:NO];
CGFloat leftHeight = 0.f;
_leftView.frame = CGRectMake(10, 10, 110, 205);
leftHeight += _leftView.frame.size.height;
_posterView.frame = CGRectMake(0, 0, _leftView.frame.size.width, _leftView.frame.size.height - 40);
GImage* img = (GImage*)[_currentMovie.imageList objectAtIndex:0];
//_posterView.image = img.localImage;
//#autoreleasepool {
_posterView.image = [UIImage imageWithData:UIImagePNGRepresentation(img.localImage)];
_btnVertoningen.frame = CGRectMake(0, _posterView.frame.origin.y + _posterView.frame.size.height + 5, _leftView.frame.size.width, 35);
_posterView.userInteractionEnabled = YES;
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(posterClick)];
singleTap.numberOfTapsRequired = 1;
[_posterView addGestureRecognizer:singleTap];
[_leftView addSubview:_posterView];
CGFloat rightHeight = 0.f;
_rightView.frame = CGRectMake(_leftView.frame.origin.x + _leftView.frame.size.width + 5, _leftView.frame.origin.y, self.view.frame.size.width - (_leftView.frame.size.width + _leftView.frame.origin.x) - 15, 1);
_lblRegie.frame = CGRectMake(0, rightHeight, 48, 16);
NSMutableString* text = [[NSMutableString alloc]init];
NSArray* arr = _currentMovie.directorList;
etc.....
}
Probably your app have memory leak. Try use imageWithContentsOfFile instead of imageNamed:
_imgView.image = [UIImage imageWithContentsOfFile:imagePath];
Another possibility to have memory leaks with ARC is using a separate thread without an autorelease pool set up. Check this link:
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html:
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.
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 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