UIView animation block not waiting - iphone

I have this code:
[UIView animateWithDuration:0.8
animations:^{
[self methodToRun];
}
completion:^(BOOL finished){
[self anotherMethod];
}];
Although there's things in methodToRun, the app just doesn't wait the 0.8 seconds and proceeds to run anotherMethod. Is there any way I can simply just get to wait 0.8 seconds before running the second bit?

Don't misuse an animation block like that. If you really want a delay, use the method Ken linked to or even an easier way is to use GCD (iOS 4+).
Here's an example using a delay of 0.8 like you used in your question:
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 0.8);
dispatch_after(delay, dispatch_get_main_queue(), ^(void){
[self anotherMethod];
});

You could add a call to [NSTimer performSelector:withObject:afterDelay] instead of relying on the completion parameter.

Related

Multiple action using selector after delay

Is there a way to do multiple actions respectively using the : performSelector:withObject:afterDelay code ?
Sample code will be appreciated ,
Thanks in advance.
Or use blocks. If you start to type dispatch_after, you'll see code completion that will pop up the following snippet of code, and then you can put however many actions you want in that block. In this example, I'm showing it being used inside an IBAction:
- (IBAction)pushedSomeButton:(id)sender
{
// anything you want to do immediate, do here
[self doingSomethingRightNow];
// anything you want to defer for some time, do inside the dispatch_after block
// in this example, calling callAnotherMethod and whyNotCallAnotherMethod
int64_t delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self callAnotherMethod];
[self whyNotCallAnotherMethod];
});
}
Setup a method that gets fired with the performSelector:withObject:afterDelay: call:
-(void)performTheseAction {
// do something
// do something else
[self callAnotherMethod];
[self whyNotCallAnotherMethod];
}

Xcode NSTimer get position object every millisecond

I need to get the position of the _propArrow (UIImageView) every .1sec at all time and use a NSLog to verify in the method (void)runScheduledTask
but does not operate every .1 sec.
only indicates the start and end of the movement of the animation
This is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
_propArrow.frame = CGRectMake(144, 291, 33, 71);
aTimer = [NSTimer scheduledTimerWithTimeInterval:.1 target:self selector:#selector(runScheduledTask) userInfo:nil repeats:YES];
}
- (void)runScheduledTask {
NSLog(#"Position: %#", NSStringFromCGRect(_propArrow.frame));
}
- (IBAction)fire:(id)sender {
[UIView animateWithDuration:1
delay:0
options:UIViewAnimationCurveEaseInOut
animations:^{
_propArrow.frame = CGRectMake(144, 0, 33, 71);
}
completion:^(BOOL finished){
}];
}
First, according to the NSTimer docs, that class has an effective resolution of about 50-100 ms. So using it to do anything at 1 ms intervals is bound to fail.
Second, even if NSTimer could handle 1 ms intervals, you're not even asking it to do that:
aTimer = [NSTimer scheduledTimerWithTimeInterval:.05...
Your code schedules the timer to run ever 0.05 s, which is to say every 50 ms.
Third, the screen doesn't update at anything close to 1 ms intervals, so the actual position of your image on the screen will change much less often than that.
So, what exactly are you trying to accomplish here? If you want to know where the object would be in the real world with continuous motion, you can calculate that for any point in time.
Because in most UIKit animated properties, the new value gets set immediately from the perspective of your code; it's the displayed value that actually animates. Reading animatable properties like UIView.frame should never give you an intermediate value (the main exception is UIScrollView which does its own thing for animated scrolling).
If you want "approximately what's being displayed on screen", you need to access the CoreAnimation "presentation layer":
#import <QuartzCore/CALayer.h>
- (void)runScheduledTask {
NSLog(#"Position: %#", NSStringFromCGRect(_propArrow.layer.presentationLayer.frame));
}
Also consider using CADisplayLink if you want a callback that runs every frame.

Triggering code when UIPickerView selectRow:inComponent:animated:YES is completed?

My action method:
- (IBAction)buttonPressed {
// animate picker to random row (picker is a UIPickerView)
int row = random() % [self.column1 count];
[picker selectRow:row inComponent:0 animated:YES];
[picker reloadComponent:0];
// display new selected row content
int selectedRow = [picker selectedRowInComponent:0];
NSString *selectedItem = [self.column1 objectAtIndex:selectedRow];
myLabel.text = selectedItem; // UILabel under the picker
}
However
myLabel.text = selectedItem;
gets called before the animation has completed and so it doesn't display the new value.
I found the thread How to get callback from UIPickerView when the selectRow animation is done?, but the answer uses a beginAnimations/commitAnimation block - about which apple says: "Use of this method is discouraged in iOS 4.0 and later. You should use the block-based animation methods to specify your animations instead."
How would I use block-based animation to accomplish this?
It looks like Apple needs to update their UIPickerView API to support a block based completion handler. Until then, just wait the estimated time of the animation.
[picker selectRow:0 inComponent:0 animated:YES];
[self performSelector:#selector(setMyLabel) // setMyLabel - my function
withObject:nil
afterDelay:0.4];
Have you tried something like this:
[UIView animateWithDuration:2.0
delay:0.0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
// the animation code
[myPickerView selectRow:0 inComponent:0 animated:YES];
}
completion:^(BOOL finished) {
[self performSelector:#selector(setMyLabel)];
}
];
Or you could just set the label directly in the completion block, if that's the only thing you want to do after the animation completes.
EDIT:
The code above is the same as the code in the other thread you've mentioned, but using animation blocks instead. I actually didn't know if the method selectRow:inComponent:animated: will run on the same animation block thread or it will have its own thread. Since it didn't work, that means it runs on its own thread so the code I wrote won't work.
There is one way around to solve this, which is by blocking the main thread for the time needed for the animation to complete. You can call [NSThread sleepForTimeInterval:1.0] after your call to selectRow:inComponent:animated:, and you can adjust the interval until you reach a suitable value. Note that blocking the main thread is highly discouraged by Apple. Personally I would block it only if I couldn't think of another way to achieve what I'm trying to do and if the blocking time is smaller than 1.

How can I trigger a method every 10 seconds without using NSTimer?

I would like to call a method every 10 seconds, but I want to use something other than NSTimer. What could I use to do this?
I know you said you didn't want to use timers, but just to make sure you know how simple it would be with a timer...
[NSTimer scheduledTimerWithTimeInterval:10.0
target:self
selector:#selector(someMethod)
userInfo:nil
repeats:YES];
If you dont want to use the timer, you can use GCD which internally will make use of NSOperationQueue, nevertheless will work in all cases. For eg: i had a class which was inherited from NSOperation so the above methods didn't work so i had go go with GCD:
double delayInSeconds = 3.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_after(popTime, queue, ^{
[self methodYouWantToCall];
});
The above code calls the method methodYouWantToCall after every three seconds.
You can create a loop with performSelector:withObject:afterDelay: setting afterDelay to 10.0.
I don't recommend this though, use an NSTimer.
- (void)callMeEvery10Seconds
{
[self performSelector:#selector(callMeEvery10Seconds)
withObject:nil
afterDelay:10.0];
// ... code comes here ...
}
If you are not using Cocos2D, you have to use a NSTimer to do this....
If you are using Cocos2D, use the schedule method
here's a link below that shows both :
How can I create a count down timer for cocos2d?
The easiest way to do so is:
- (void)scheduleLoopInSeconds:(NSTimeInterval)delayInSeconds
{
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_after(popTime, queue, ^{
[self callWhatEverMethodYouWant];
[self shceduleLoopcaInSeconds:delayInSeconds];//set next iteration
});
}
// now whenever you like call this, and it will be triggering "callWhatEverMethodYouWant" every 10 secs.
[self shceduleLoopcaInSeconds:10.0];

Blocks instead of performSelector:withObject:afterDelay: [duplicate]

This question already has answers here:
How do you trigger a block after a delay, like -performSelector:withObject:afterDelay:?
(20 answers)
Closed 9 years ago.
I often want to execute some code a few microseconds in the future. Right now, I solve it like this:
- (void)someMethod
{
// some code
}
And this:
[self performSelector:#selector(someMethod) withObject:nil afterDelay:0.1];
It works, but I have to create a new method every time. Is it possible to use blocks instead of this? Basically I'm looking for a method like:
[self performBlock:^{
// some code
} afterDelay:0.1];
That would be really useful to me.
There's no built-in way to do that, but it's not too bad to add via a category:
#implementation NSObject (PerformBlockAfterDelay)
- (void)performBlock:(void (^)(void))block
afterDelay:(NSTimeInterval)delay
{
block = [[block copy] autorelease];
[self performSelector:#selector(fireBlockAfterDelay:)
withObject:block
afterDelay:delay];
}
- (void)fireBlockAfterDelay:(void (^)(void))block {
block();
}
#end
Credit to Mike Ash for the basic implementation.
Here's a simple technique, based on GCD, that I'm using:
void RunBlockAfterDelay(NSTimeInterval delay, void (^block)(void))
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC*delay),
dispatch_get_current_queue(), block);
}
I'm not a GCD expert, and I'd be interested in comments on this solution.
Another way (perhaps the worst way to do this for many reasons) is:
[UIView animateWithDuration:0.0 delay:5.0 options:UIViewAnimationOptionAllowUserInteraction animations:^{
} completion:^(BOOL finished) {
//do stuff here
}];
If you specifically need a longer delay, the solutions above work just fine. I've used #nick's approach with great success.
However, if you just want your block to run during the next iteration of the main loop, you can trim it down even further with just the following:
[[NSOperationQueue mainQueue] addOperationWithBlock:aBlock];
This is akin to using performSelector: with afterDelay of 0.0f
I used similar code like this:
double delayInSeconds = 0.2f;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
//whatever you wanted to do here...
});
There's a nice, complete category that handles this situation here:
https://gist.github.com/955123