iOS application crashes when using [UIImageView startAnimating] method - objective-c

in my nib file I have a UIImageView that I have linked to my variable IBOutlet UIImageView* imgH6; I need to play several animations therefore I have decided to play them on imgH6. for example if I want to start animating a group of images I will do: [self playAnimation1];
-(void) playAnimation1{
NSArray *imagesArrayDilema;
imagesArrayDilema = [NSArray arrayWithObjects:
[UIImage imageNamed:#"dilema1-02.png"],
[UIImage imageNamed:#"dilema1-03.png"],
[UIImage imageNamed:#"dilema1-04.png"],
[UIImage imageNamed:#"dilema1-05.png"]
// .....
// there are several images
, nil];
imgH6.animationImages = imagesArrayDilema;
// I will then start the animation as:
wait = ((double)[imgH6.animationImages count])/30;
[imgH6 setAnimationRepeatCount:1];
imgH6.animationDuration = wait;
[imgH6 startAnimating];
}
-(void) playAnimation2{
NSArray *imagesArrayDilema;
imagesArrayDilema = [NSArray arrayWithObjects:
[UIImage imageNamed:#"differentImage-01.png"],
[UIImage imageNamed:#"differentImage-02.png"],
[UIImage imageNamed:#"differentImage-03.png"],
[UIImage imageNamed:#"differentImage-04.png"],
// .....
// there are several images
, nil];
imgH6.animationImages = imagesArrayDilema;
// I will then start the animation as:
wait = ((double)[imgH6.animationImages count])/30;
[imgH6 setAnimationRepeatCount:1];
imgH6.animationDuration = wait;
[imgH6 startAnimating];
}
///.....
// ...
-(void) playAnimation8{ // etc...
so when I call [self playAnimation1]; there is no problem. the animation animates fine. I may also call [self playAnimation7]; and a different group of images animate. I am able to do this several times but eventually the application crashes! In order to fix this I have done the following: I have a global BOOL variable called isAnimating and what that does is that every time there is an animation in place you will not be able to animate a different group of images until the animation is done. Before I used to have the method:
-(void) animateMain :(NSArray*) imagesArray{
imgH6.animationImages = imagesArray;
// I will then start the animation as:
double wait = ((double)[imgH6.animationImages count])/30;
[imgH6 setAnimationRepeatCount:1];
imgH6.animationDuration = wait;
[imgH6 startAnimating];
}
and the application still crashed after playing several times. I have noticed that it crashes when changing the animation several times. In other words if I alway splay the same animation it does not crash...
also I do not used the word alloc or init to instantiate the array so I don't think this has to do with releasing the array.

Related

Stopping performSelectorInBackground

In objective-c, when a button is pressed, I load a processing animation while a file is uploaded using:
[self performSelectorInBackground:#selector(loadAnimation) withObject:self];
This works and a loadAnimation image displays.
How do I stop it once the file has uploaded? I have tried:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(loadAnimation) object:nil];
But this does not stop the animation.
The loadAnimation is:
- (void) loadAnimation
{
loadingPng.hidden=NO;
NSArray *imageArray = [[NSArray alloc] initWithObjects:[UIImage imageNamed:#"0.png"], [UIImage imageNamed:#"1.png"], [UIImage imageNamed:#"2.png"], [UIImage imageNamed:#"3.png"], [UIImage imageNamed:#"4.png"], [UIImage imageNamed:#"5.png"], nil];
loadingPng = [[UIImageView alloc] initWithFrame:CGRectMake(487, 500, 50, 50)];
[self.view addSubview:loadingPng];
loadingPng.animationImages = imageArray;
loadingPng.animationDuration = 1.5;
[loadingPng startAnimating];
}
Just to make things clear performSelectorInBackground creates another thread, which executes the method and dies (if not attached to any runloop).
If during this execution something on the screen appears and you want to hide it, just call a whatever routine needed to hide it. If you use UIActivityIndicatorView or UIImageView call -(void)stopAnimating (in this case [loadingPng stopAnimating]).
There is no need in starting animation in background, actually it's a thing that should not be done, because anything that involves UI manipulation is highly recommended to be done only on the main thread.
It's a loading that should go to background, and animation triggering stays on the main thread.

Subclassing UIActivityIndicator to change the Image

This is what I did:
#implementation BGUIActivityIndicator
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
[self customInitialize];
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
[self customInitialize];
return self;
}
-(void)customInitialize
{
UIView * theImageView= [self findASubViewforClass:[UIImageView class]];//
UIImageView * theImageView1 = (UIImageView *) theImageView;
theImageView1.image = [UIImage imageNamed:#"spinner_blue"];
[theImageView1.image saveScreenshot];
while (false) ;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
#end
Everything seems perfect. [theImageView1.image saveScreenshot]; jot down both the old and new view perfectly.
However, nothing changes. Why?
I am not exactly asking how to change the image of UIActivityIndicator. There are tons of it already. I want to use it by subclassing UIActivityIndicator because I think it's the most elegant solution. I can't seem to do that.
In particular, I am asking why my approach, which works for changing background of search controller, for example, doesn't work for this.
According to the UIActivityIndicatorView Class Reference ,there is no way/ chance to change the image through sub-classing.
However you can change its activityIndicatorViewStyle , color of the activity indicator,UIActivityIndicatorStyle etc..
I think, without sub-classing, the class class UIImageView provides a very useful and simple way to implement such a thing. The only thing you have to do is to:
1.Provide a number of images that reflect your indicator animation.
2.Create a new UIImageView instance and set images and animation duration.
3.Position your custom activity indicator within your current view.
SAMPLE CODE:
//Create the first status image and the indicator view
UIImage *statusImage = [UIImage imageNamed:#"status1.png"];
UIImageView *activityImageView = [[UIImageView alloc]
initWithImage:statusImage];
//Add more images which will be used for the animation
activityImageView.animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"status1.png"],
[UIImage imageNamed:#"status2.png"],
[UIImage imageNamed:#"status3.png"],
[UIImage imageNamed:#"status4.png"],
[UIImage imageNamed:#"status5.png"],
[UIImage imageNamed:#"status6.png"],
[UIImage imageNamed:#"status7.png"],
[UIImage imageNamed:#"status8.png"],
nil];
//Set the duration of the animation (play with it
//until it looks nice for you)
activityImageView.animationDuration = 0.8;
//Position the activity image view somewhere in
//the middle of your current view
activityImageView.frame = CGRectMake(
self.view.frame.size.width/2
-statusImage.size.width/2,
self.view.frame.size.height/2
-statusImage.size.height/2,
statusImage.size.width,
statusImage.size.height);
//Start the animation
[activityImageView startAnimating];
//Add your custom activity indicator to your current view
[self.view addSubview:activityImageView];
See the full details Here

array crashing at index which is in bounds?

Can anyone help me out with explaining why my array is crashing.
Basically I have two buttons which change the image in the imageView.
h:
#interface CaseStudySecondPageViewController : UIViewController
{
//scroller and back and forward buttons for custom control.
__weak IBOutlet UIScrollView *scroller;
__weak IBOutlet UIButton *back;
__weak IBOutlet UIButton *forward;
//app delegate to return the selected case study from the other controller.
AppDelegate *del;
//variables for displaying the case studies
NSArray *myImageArray;
NSInteger localSelctorInt;
}
//setup a IBOutlet to allow the image to be changed.
#property (weak, nonatomic) IBOutlet UIImageView *logoImage;
#end
m:
update method:
-(void)Updater
{
[logoImage setImage:[myImageArray objectAtIndex:localSelctorInt]];
}
previous and next buttons:
//returns to previous image if back button is clicked.
-(void) back:(id)sender
{
if (localSelctorInt > 0)
{
localSelctorInt--;
}
else
{
localSelctorInt = 0;
}
[self Updater];
}
//returns to next image if forward button is clicked. increase 7 if array size changes.
//1 removed from int as in starts at 1 and array starts at 0.
-(void) forward:(id)sender
{
if (localSelctorInt < 7)
{
localSelctorInt++;
}
else
{
localSelctorInt = 7;
}
[self Updater];
}
and finally my image array is declared in:
- (void)viewDidLoad
{
[super viewDidLoad];
//setup the delegate to allow access to the int which was modified on the page before.
del = [[UIApplication sharedApplication] delegate];
//assign a local variable to the int from the previous page.
localSelctorInt = *(del.selectorInt);
//create back and forward buttons as UIButtons first.
[back addTarget:self action:#selector(back: ) forControlEvents:UIControlEventTouchUpInside];
[forward addTarget:self action:#selector(forward:) forControlEvents:UIControlEventTouchUpInside];
//pass the ui buttons into ui bar items.
UIBarButtonItem *UIBack = [[UIBarButtonItem alloc] initWithCustomView:back];
UIBarButtonItem *UIForward = [[UIBarButtonItem alloc] initWithCustomView:forward];
//add these to an array (notice item"s").
self.navigationItem.rightBarButtonItems = [NSArray arrayWithObjects:UIForward, UIBack, nil];
//initialize the array with all of the images for the case studies logos.
myImageArray = [NSArray arrayWithObjects:
[UIImage imageNamed:#"Logo_Arm.png"],
[UIImage imageNamed:#"Logo_Fife"],
[UIImage imageNamed:#"Logo_Findel.png"],
[UIImage imageNamed:#"Logo_BirkBeck.png"],
[UIImage imageNamed:#"Logo_NHS_Dudley.png"],
[UIImage imageNamed:#"Logo_NHS_Kensignton.png"],
[UIImage imageNamed:#"Logo_Yorkshire_Water.png"],
[UIImage imageNamed:#"Logo_Uni_Hertfordshire.png"],
nil];
//call update method once to load the first image selected from the previous page.
[self Updater];
}
now my app crashes when it trys to index the 4th image (3rd in the array):
* Terminating app due to uncaught exception 'NSRangeException', reason: '* -[__NSArrayI objectAtIndex:]: index 3 beyond bounds [0 ..
2]'
It's probably something really simple, but id appreciate your help, cheers.
If you look at your exception, it says that your array contains 3 objects only ( ... bounds [0..2] ... ). So, my guess is that your fourth image doesn't exist ... Check spelling of [UIImage imageNamed:#"Logo_BirkBeck.png"]. Does this image really exist? It returns nil, list of objects is nil terminated, so, your array does contain 3 images only instead of 8 images.
Side Note: Don't use magic constants like localSelctorInt < 7. Always rely on real number of elements in array (_myImageArray.count). It's a way to hell ...
2nd Side Note: Don't declare ivars without underscore prefix. Do use something like this _myImageArray for ivar name.
I guess its due to not allocating the array
myImageArray = [[NSArray alloc] initWithObjects:
[UIImage imageNamed:#"Logo_Arm.png"],
[UIImage imageNamed:#"Logo_Fife.png"],
[UIImage imageNamed:#"Logo_Findel.png"],
[UIImage imageNamed:#"Logo_BirkBeck.png"],
[UIImage imageNamed:#"Logo_NHS_Dudley.png"],
[UIImage imageNamed:#"Logo_NHS_Kensignton.png"],
[UIImage imageNamed:#"Logo_Yorkshire_Water.png"],
[UIImage imageNamed:#"Logo_Uni_Hertfordshire.png"],
nil];

how load many photos from url in background (asynchronous)

i have this method i use to load many images to scroll view, but when the images load from the url my scroll view is stuck and i can't understand how to load them in the background so the user will not feel it.
this method will call few times (8) in "for" cycle.
- (void)loadPhotosToLeftscroll{
//getting image information from JSON
NSMutableDictionary *photoDict;
photoDict = [leftPhotoArray lastObject];
//getting the photo
NSString *photoPath = [photoDict objectForKey:#"photos_path"];
NSLog(#"photo Path:%#",photoPath);
NSData * imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:photoPath]];
UIImage *image = [UIImage imageWithData:imageData];
// use the image how you like, say, as your button background
//calculating the hight of next photo
UIImageView *leftImage = [leftBlockScroll.subviews lastObject];
//allocating photoView
UIImageView *photoView = [[UIImageView alloc]initWithFrame:CGRectMake(5 , leftImage.frame.origin.y + leftImage.frame.size.height+5, image.size.width/2, image.size.height/2 )];
photoView.userInteractionEnabled=YES;
[photoView.layer setMasksToBounds:YES];
[photoView.layer setCornerRadius:3];
//getting items list
NSDictionary *sh_items = [photoDict objectForKey:#"items"];
//adding image button
UIButton *imageOverButton = [UIButton buttonWithType:UIButtonTypeCustom];
imageOverButton.frame = CGRectMake(0, 0, photoView.frame.size.width, photoView.frame.size.height);
[imageOverButton addTarget:self action:#selector(LeftimagePressed:) forControlEvents:UIControlEventTouchUpInside];
[imageOverButton setTag:[leftPhotoArray count]-1];
[photoView addSubview:imageOverButton];
//adding sh button to imageView
[self addThe_sh_signs:sh_items To_ImageView:photoView];
//subViewing the image to the scrollView
[self insert_Image:image toImageView:photoView in_Scroll:leftBlockScroll];
//calclulating the position of next imageView in scroll.
nextLeftPhotoHight = photoView.frame.size.height + photoView.frame.origin.y + 5;
//calculating the hight of the highest scroll view in both.
leftBlockScroll.contentSize = CGSizeMake(160, [self theSizeOfScrollViewHight]);
rightBlocScroll.contentSize = CGSizeMake(160, [self theSizeOfScrollViewHight]);
isLoadindContant = NO;
[self.view reloadInputViews];
[leftBlockScroll reloadInputViews];
}
please do not send me to some link that trying to explain how to use the asynchronous.
Try to explain according the method you see here.
Im here for any question, that you need to ask to help me.
You will have to do it asynchronously in a proper way. I do not think there is any way around that. I subclassed an UIImageView object and placed many instances of it within the cells of a talbe (in your case within the scroll view). The subclass objects are initialized with an url and load their image asynchronously (with some caching so that the image is not loaded every time).
This tutorial helped me much in the beginning:
http://www.markj.net/iphone-asynchronous-table-image/
You will just have to adopt that to your scroll view. The underlaying principle remains the same.

Program crashes when segueing back and dispatch queue has animation method

Ok so i have a uitableview and when an item is selected it segues to a new view controller to show an image (inside of a uiscrollview). The image begins downloading in a dispatch_queue from prepareforsegue. To be clear I am doing all UI updates in the main queue. The problem is that if i hit the back button quick enough then my program crashes with an exc_bad_address. I think the problem has to deal with zoomToRect animated:YES because when i set animated to NO i cant get it to crash. Plus the call stack below deals with animation. What is the best way to go about fixing this and is there a better way to get what i need done?
Also when debugging the problem print 'Block completed' and then crashes shortly after.
stack trace
Here is the method called. It is called in a setter of the destination controller in prepareForSegue.
-(void) updateDisplay {
dispatch_queue_t queue = dispatch_queue_create("Load Flickr Photo", NULL);
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:spinner];
[spinner startAnimating];
dispatch_async(queue, ^{
UIImage *image = [FlickrFetcher imageForPhoto:self.currentPhoto format:FlickrPhotoFormatLarge];
dispatch_async(dispatch_get_main_queue(), ^{
self.navigationItem.rightBarButtonItem = nil;
self.imageView.image = image;
self.title = [FlickrFetcher titleForPhoto:self.currentPhoto];
CGAffineTransform transform = CGAffineTransformMakeScale(1.0, 1.0);
self.imageView.transform = transform;
self.imageView.frame = CGRectMake(0, 0, self.imageView.image.size.width, self.imageView.image.size.height);
self.scrollView.maximumZoomScale = 4.0;
self.scrollView.minimumZoomScale = .2;
self.scrollView.zoomScale = 1;
self.scrollView.contentSize = self.imageView.bounds.size;
//i think problem is here
[self.scrollView zoomToRect:self.imageView.frame animated:YES];
NSLog(#"Block completed");
});
});
}
I think a potential problem could be that while your block retains your self the view controller while its active in queue...
If you send -zoomToRect:animated:NO, the block will still be active and blocking on the call which ensures that all the objects are still valid in memory.
If you send -zoomToRect:animated:YES, the block will exit potentially generating a race condition where your view controller would be released since storyboards will also release your view controller when you go back in a segue leaving it with an effective retain count of zero.