I am doing one application.In that i am doing the animation for uiimageview to show the different image like below
UIImageView * fearthersanimate = nil;
[fearthersanimate setCenter:sender.center];
fearthersanimate = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 120,200)];
[fearthersanimate setCenter:CGPointMake(sender.center.x, sender.center.y)];
fearthersanimate .animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"water_can_1.png"],
[UIImage imageNamed:#"water_can_2.png"],[UIImage imageNamed:#"water_can_3.png"],
[UIImage imageNamed:#"water_can_4.png"],[UIImage imageNamed:#"water_can_5.png"],
[UIImage imageNamed:#"water_can_6.png"],[UIImage imageNamed:#"water_can_7.png"],
[UIImage imageNamed:#"water_can_8.png"],
nil];
fearthersanimate.animationDuration = 1.0f;
fearthersanimate.animationRepeatCount = 1;
[self.view addSubview: fearthersanimate];
[fearthersanimate startAnimating];
But remaining operation is starting before end of this animation.But i need to do this animation first and until i need to stop the remaining process.
You can call your rest of operation in other method and call it after the same delay as you set animation time for images.
Below is the line of code by which you can call other method after some time interval
[self performSelector:#selector(restOfOperations) withObject:nil afterDelay:1.0];
//restOfOperations method defination/implementation
-(void)restOfOperations
{
//your code you want to perform after image animation
}
You can use the UIView animation block. For example:
[UIView animateWithDuration:0.5 animations:^{
} completion:^(BOOL finished) {
}];
Put the remaining process in the completion block.
Those animations are performed asynchronously, so you just need to fight async with async.
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(feathersanimate.animationDuration * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
// Remaining logic
});
Related
I'm attempting to have a class method called with args on a delay, asynchronously, to hide a UILabel. Essentially, the label should appear, and then disappear in three seconds. I'm using the below to accomplish this.
Main method setting up the displayed view
+(void)queueError:(UILabel*)messageView errorText:(NSString*)errorText{
[messageView setText:errorText];
messageView.hidden = NO;
messageView.tag = arc4random_uniform(UINT32_MAX);
[UIView animateWithDuration:0.3 delay:0.0 options:0 animations:^(){
messageView.alpha = 1.0;
}completion:^(BOOL finished){
NSArray* args = [NSArray arrayWithObjects:messageView, [NSNumber numberWithInt:messageView.tag ], nil];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[UBSNavigationUtils class] performSelector:#selector(dequeueErrorTime:) withObject:args afterDelay:3];
});
}];
}
Method to be called after three second delay
+(void)dequeueErrorTime:(NSArray*)args{
UILabel* messageView = args[0];
NSInteger tag = [((NSNumber*)args[1]) integerValue];
if(messageView.tag == tag){
[[UBSNavigationUtils class] fadeOutError:messageView];
}
}
However, my method is never being called.
You put selector in the dispatch_queue runloop (technically as a timer), that isn't running. Therefore your method never called. I think, if you try invoke [[NSRunLoop currentRunLoop]run]; , method will get called.
From apple discussion about performSelector:afterDelay:
When the timer fires, the thread attempts to dequeue the message from the run loop and perform the selector. It succeeds if the run loop is running and in the default mode; otherwise, the timer waits until the run loop is in the default mode.
Instead of using dispatch_async, consider dispatch_after.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[UBSNavigationUtils dequeueErrorTime:args];
});
Edit: I just wanted to help you clean up your code. :(
+(void)queueError:(UILabel*)messageView errorText:(NSString*)errorText{
[messageView setText:errorText];
messageView.hidden = NO;
NSInteger expectedTag = arc4random_uniform(UINT32_MAX);
messageView.tag = expectedTag;
[UIView animateWithDuration:0.3 delay:0.0 options:0 animations:^(){
messageView.alpha = 1.0;
} completion:^(BOOL finished) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[UBSNavigationUtils dequeueErrorMessage:messageView tag:expectedTag];
});
}];
}
+ (void)dequeueErrorMessage:(UILabel *)messageView tag:(NSInteger)tag {
if(messageView.tag == tag) {
[[UBSNavigationUtils class] fadeOutError:messageView];
}
}
I want to use UIActivityIndicator on a function.
I'm implementing some Core Filters, some of which take almost a second to implement. I want that UIActivityIndicator to start and stop according the function.
I looked up online, but it's mostly using a timer. So that would make it hard-wired and not based on how long it actually takes to implement the function.
Can someone tell me a small example how I can do that ?
Declare ActivityIndicator
#property (nonatomic, strong) UIActivityIndicatorView *activityIndicatorView;
then,
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor blackColor];
// Do any additional setup after loading the view, typically from a nib.
CGRect frame = CGRectMake (120.0, 185.0, 80, 80);
self.activityIndicatorView = [[UIActivityIndicatorView alloc] initWithFrame:frame];
[self.view addSubview:self.activityIndicatorView];
}
using this code you can start and stop the activityIndicator
[self.activityIndicatorView startAnimating]; //start
[self.activityIndicatorView stopAnimating]; //stop
all UI animation and navigation would be performed once the method execution is over. So if you want such functionality go for timer or dispatch queue.
as follows
UIActivityIndicatorView *activity = [[UIActivityIndicatorView alloc] initWithFrame:self.window.frame];
[activity startAnimating];
//Your methods to be executed
double delayInSeconds = 0.01;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[activity stopAnimating];
});
in this method execution is kept in a queue after the execution of current method, method written dispatch_time_t will be executed.
Im attempting add image views to a UIView using this code:
for (int i = 0; i <numberOfImages; i++) {
UIImageView *image = [UIImageView alloc]initWithFrame:CGRectMake(40, 40, 40, 40)];
image.image = [images objectAtIndex:i];
[self.view addSubview:image];
}
This works but the problem is I would like to have a 5 second delay before it adds each image, instead it adds them all at the same time. Can anybody help me out? Thanks.
Example:
5 seconds = one image on screen
10 seconds = two images on screen
15 seconds = three images on screen
It will be more efficient to use an NSTimer.
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:numberOfSeconds
target:self
selector:#selector(methodToAddImages:)
userInfo:nil
repeats:YES];
This will essentially call methodToAddImages repeatedly with the specified time interval. To stop this method from being called, call [NSTimer invalidate] (bear in mind that an invalidated timer cannot be reused, and you will need to create a new timer object in case you want to repeat this process).
Inside methodToAddImages you should have code to go over the array and add the images.
You can use a counter variable to track the index.
Another option (my recommendation) is to have a mutable copy of this array and add lastObject as a subview and then remove it from the mutable copy of your array.
You can do this by first making a mutableCopy in reversed order as shown:
NSMutableArray* reversedImages = [[[images reverseObjectEnumerator] allObjects] mutableCopy];
Your methodToAddImages looks like:
- (void)methodToAddImages
{
if([reversedImages lastObject] == nil)
{
[timer invalidate];
return;
}
UIImageView *imageView = [[UIImageView alloc] initWithFrame:(CGRectMake(40, 40, 40, 40))];
imageView.image = [reversedImages lastObject];
[self.view addSubview:imageView];
[reversedImages removeObject:[reversedImages lastObject]];
}
I don't know if you're using ARC or Manual Retain Release, but this answer is written assuming ARC (based on the code in your question).
You can use dispatch_after to dispatch a block, executed asynchronously that adds the image. Example:
for(int i = 0; numberOfImages; i++)
{
double delayInSeconds = 5.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void)
{
UIImageView *image = [UIImageView alloc]initWithFrame:CGRectMake(40, 40, 40, 40)];
image.image = [images objectAtIndex:i];
// Update the view on the main thread:
[self.view performSelectorOnMainThread: #selector(addSubview:) withObject: image waitUntilDone: NO];
});
}
Separate your code into a function, and call via NSTimer.
for (int i = 0; numberOfImages; i++) {
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:5
target:self
selector:#selector(showImage)
userInfo:[NSNumber numberWithInt:i]
repeats:NO];
And then your function:
-(void) showImage:(NSTimer*)timer {
//do your action, using
NSNumber *i = timer.userInfo;
//Insert relevant code here
if (!done)
NSTimer *newTimer = [NSTimer scheduledTimerWithTimeInterval:5
target:self
selector:#selector(showImage)
userInfo:[NSNumber numberWithInt: i.intValue+1]
repeats:NO];
}
}
userInfo is a convenient way of passing parameters to functions that you need to call (but they do have to be Objects). Also, by using repeats:NO, you don't have to worry about invalidating the timer, and there's no risk of leaving timer running in memory.
also this is best option. Try this
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:5.0]];
I think you'd be better off with an animation
for (int i = 0; i < numberOfImages; i++)
{
// Retrieve the image
UIImageView *image = [[UIImageView alloc] initWithFrame:CGRectMake(40, 40, 40, 40)];
image.image = [images objectAtIndex:i];
// Add with alpha 0
image.alpha = 0.0;
[self.view addSubview:image];
[UIView animateWithDuration:0.5 delay:5.0*i options:UIViewAnimationOptionAllowUserInteraction animations:^{
// Fade in with delay
image.alpha = 1.0;
} completion:nil];
}
Not exactly what you asked for, since all the views will be added immediately, and then faded-in, but I feel that you're actually trying to achieve that, like some sort of stacking of images, right?
In fact, if you plan on removing the previous image, you can do it in the completion block, like this:
UIImageView *imagePrevious = nil;
for (int i = 0; i < numberOfImages; i++)
{
// Retrieve the image
UIImageView *image = [[UIImageView alloc] initWithFrame:CGRectMake(40, 40, 40, 40)];
image.image = [images objectAtIndex:i];
// Add with alpha 0
image.alpha = 0.0;
[UIView animateWithDuration:0.5 delay:5.0*i options:UIViewAnimationOptionAllowUserInteraction animations:^{
// Add and fade in with delay
[self.view addSubview:image];
image.alpha = 1.0;
} completion:^(BOOL finished)
{
if (finished && imagePrevious)
{
[imagePrevious removeFromSuperview];
}
}];
imagePrevious = image;
}
I am trying to execute ball blasting effect to play sequentially. One after another.
What I have done yet:
For ball blasting effect i had used
UIButton *ballButton = (UIButton *)[cell viewWithTag:10];
ballButton.imageView.animationImages = [[NSArray alloc] initWithObjects:
[UIImage imageNamed:#"1.png"],
[UIImage imageNamed:#"2.png"],
[UIImage imageNamed:#"3.png"],
[UIImage imageNamed:#"4.png"],
nil];
ballButton.imageView.animationDuration = 1;
ballButton.imageView.animationRepeatCount = 1;
and this line on code is attached to multiple buttons in a cell of a collection view.
I call these ballbutton.imageview start the animation like this
[UIView animateWithDuration:1 delay:5 options:UIViewAnimationOptionCurveEaseOut animations:^{
NSIndexPath *path2 = [NSIndexPath indexPathForRow:x inSection:0];
UICollectionViewCell *cell = [ballContainer cellForItemAtIndexPath:path2];
UIButton *ballObject = (UIButton *) [cell viewWithTag:10];
[ballObject.imageView startAnimating];
} completion:^(BOOL b){
NSLog(#" here i call next animation of ball blast to execute ");
}];
I nested 3 animations buttons like this.
First of all, why don't you create one big animation for all other three? The thing with the UIView animateWithDuration: is that it performs the animation block in the period of time you gave to it, i.e. setting the frame from (200,200) to (0,0) will move it proportionally in the time of a second, in your case. But UIImageView's properties regarding animations are made in such way that the animation is already done for you.
Personally, I would suggest using a timer, like this:
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:ballObject.imageView.animationDuration
target:self
selector:#selector(performNextAnimation:)
userInfo:nil repeats:NO];
[ballObject.imageView startAnimating];
And in the performNextAnimation method:
- (void) performNextAnimation{
[timer invalidate]; // you have to access the timer you've scheduled with the animation
timer = nil;
/* code for starting the next animation */
}
this way i solved my issue.
initially i started animation by calling this
[self startAnim:index];
than implemented this StartAnim like this and problem solved.
-(void)StartAnim :(int)x{
NSIndexPath *path2 = [NSIndexPath indexPathForRow:x inSection:0];
UICollectionViewCell *cell = [ballContainer cellForItemAtIndexPath:path2];
UIButton *ballObject = (UIButton *) [cell viewWithTag:10];
[ballObject setBackgroundImage:nil forState:UIControlStateNormal];
ballObject.imageView.image = nil;
[ballObject.imageView startAnimating];
double delayInSeconds = 0.15;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
if(x-6>=0){
[self StartAnim :(x-6)];
}
});
}
I'm trying to load views asynchronously. The problem is that the frame of the views to be loaded depends on the data that's loaded asynchronously. In other words there are some long calculations that decide where to actually display the UIViews.
I know that there are problems when trying to actually display a UIView in a thread and that you should always load them back in the main thread, so this is the code I've been trying out:
asyncQueue = [[NSOperationQueue alloc] init];
[asyncQueue addOperationWithBlock:^{
// Do work to load the UIViews and figure out where they should be
UIButton *test = [[UIButton alloc] initWithFrame:[self doWorkToGetFrame]];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self addSubview:test];
}
}];
}];
This all resides in a UIView container.
Try something like this:
UIButton *test = [[UIButton alloc] initWithFrame:CGRectZero];
test.hidden = YES;
test.alpha = 0.0f;
[self addSubview:test];
dispatch_queue_t downloadQueue = dispatch_queue_create("myDownloadQueue",NULL);
dispatch_async(downloadQueue, ^
{
// do work to load UIViews and calculate frame
CGRect frameButton = ...;
dispatch_async(dispatch_get_main_queue(), ^
{
test.hidden = NO;
[UIView animateWithDuration:0.4f animations:^
{
test.frame = frameButton;
test.alpha = 1.0f;
}
completion:^(BOOL finished){}];
});
});
dispatch_release(downloadQueue);
This adds your button in the main thread, but makes it invisible initially. After your background work is done, you'll make it visible and set the frame using an animation.