I am using tableview and I want to call a method after some time duration.This method return a array and reload the tableview.I want within this time duration UI doesn't stuck.
Best to use Grand Central Dispatch (GCD). If you call dispatch_after(), you can run a block of code whenever you want.
// Run this code after 1 second.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self someMethod];
});
Related
I want to do three view animations, staggered a little. To do this I' mixing GCD and the view animation as follows...
EDIT - I'd like to do a flip animation with the children of viewA, then viewB, then viewC. I'd like these animations to overlap...
random external action causes: A-----------A'
B-----------B'
C---------C'
The trouble is, when a triggering action occurs during the A->C' animation, the states of the three get mixed up. I'd like a way of doing the three staggered actions as a single, uninterrupted action.
- (void)flipAnimated:(BOOL)animated {
// views A,B,C are initialized here
// kFLIPDURATION is 0.8
NSTimeInterval interval = (animated)? kFLIPDURATION / 2.0 : 0;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.01 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self flipChildrenOfView:viewA animated:animated];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, interval * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self flipChildrenOfView:viewB animated:animated];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2*interval * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self flipChildrenOfView:viewC animated:animated];
});
}
- (void)flipChildrenOfView:(UIView *)parent animated:(BOOL)animated {
// some setup here to get visible and hidden views
[UIView transitionFromView:visibleView
toView:hiddenView
duration:((animated)? kFLIPDURATION : 0)
options:UIViewAnimationOptionTransitionFlipFromLeft | UIViewAnimationOptionShowHideTransitionViews
completion:nil];
}
This works fine, except there are timer events and sometimes user actions driving these, and every so often, flipAnimated: is called while the animations are underway. The result is mixed up views and sadness.
What I tried so far was (1) reading and them becoming confused about GCD alternatives. (2) I tried an instance variable called animating, added a completion block to my view animation calling method, then did this...
#property(assign,nonatomic) BOOL animating;
- (void)flipAnimated:(BOOL)animated {
if (self.animating) return;
self.animating = YES;
NSTimeInterval interval = (animated)? kFLIPDURATION / 2.0 : 0;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.01 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self flipChildrenOfView:viewA animated:animated completion:nil];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, interval * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self flipChildrenOfView:viewB animated:animated completion:nil];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2*interval * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self flipChildrenOfView:viewC animated:animated completion:^(BOOL finished) {
self.animating = NO;
}];
});
}
But alas, same behavior. I could observe the block seeming to work with NSLogs, but the views would still get mixed up. (With a second flip starting before a prior one was done). I assume this is because I got in over my head on concurrency.
Would sure appreciate a helping hand, especially a code example. --- Almost forgot: what would be even better than stoping concurrent requests, would be to have it queued and then run after the running is finished.
The docs for transitionFromView:... do say "The view transition starts immediately unless another animation is already in-flight, in which case it starts immediately after the current animation finishes." So overlapping animation won't work like this.
Drop to CoreAnimation, e.g. CATransform3D, see Core animation animating the layer to flip horizontally.
#GrahamPerks asked me to clarify the question, and doing so clarified my thinking. In case this can help someone else, here's what I did.
The code was initializing the subviews to animate with the same staggered timing as the flips. When the code was re-entered, it was examining the view hierarchy in a partially changed state.
The fix was to decide about transition views up front, not during, the three step animation...
- (void)flipAnimated:(BOOL)animated {
// views A,B,C are initialized here
// FIX: but also initialize six views here
UIView *viewAVisible, *viewAHidden, *viewBVisible, *viewBHidden, *viewCVisible, *viewCHidden;
// FIX: refactor my animation wrapper to take the two views to transition
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.01 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self flipFrom:viewAHidden to:viewAVisible animated:animated];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, interval * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self flipFrom:viewBHidden to:viewBVisible animated:animated];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2*interval * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self flipFrom:viewCHidden to:viewCVisible animated:animated];
});
}
Now when a new animation request comes in concurrently, the views are in a coordinated state (because I think UIKit makes the change called for by the animation immediately).
I have a timer that run on 10 minutes. I want to get time at the 5th minute for some actions before end time (the 10th minute). May be not use block handler if you have another better solution.
You can use "performSelector" to do whatever you want at the 5th minute.
NSTimeInterval duration = 10 * 600;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self func];
});
I'm trying to create a method with an argument in objective-c, the argument is an amount of seconds before the actual method starts.
I'm trying to avoid using the [self performSelector:(SEL) withObject:(id) afterDelay:(NSTimeInterval)]; because having the delay within my method would actually save me a lot of coding as I am planning to add other arguments to this method.
Example of the method:
-(void) startMethodAfterArgumentDelay: (NSTimeInterval *)delay{
NSLog(#"perform action after argument delay");
}
and how to call it:
[self startMethodAfterArgumentDelay:3.0f];
Any help would be greatly appreciated!
If you don't want to use any 3rd party libraries as the other answer suggested, this is easy enough to implement yourself using GCD's dispatch_after(). Just note that this method is a asynchronous, so your method will return immediately even though the contents of the block are delayed.
- (void)startMethodAfterArgumentDelay:(NSTimeInterval)delay
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(#"perform action after argument delay");
});
}
You can use BlocksKit and then write code like that:
[self bk_performBlock:^(id obj) {
//code
} afterDelay:delay];
Just feed delay to dispatch_after.
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {
// do whatever
});
I have an iOS Xcode question I'm hoping someone can help me out with. I have a simple action button that invokes a series of methods to run, however these methods all write to a stream and retrieve the input that comes in return, so I'm having a hard time reading the stream and extracting the information.
I think this is because its all happening too fast. I would like it where i press the button and method one runs, waits half a second (for example), then method two, then method three, etc... can someone show me a simple code to do so please?
Thanks in advance, example below:
Chuck
- (IBAction)updateStatsButton:(id)sender {
[self method1];
[self method2];
[self method3];
self.label1.text = result from method 1;
self.label2.text = result from method 2;
self.label3.text = result from method 3;
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self method1];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self method2];
});
[self performSelector:#selector(method1) withObject:nil afterDelay:2];
I'm making a while loop and I'm trying to delay it.
while (mode == 1)
{
[self performSelector:#selector(on) withObject:nil afterDelay:slider.value];
NSLog(#"on");
[self performSelector:#selector(off) withObject:nil afterDelay:slider.value];
NSLog(#"off");
}
But even though I'm setting the slider to 10 seconds it goes on and off very fast.
Also my app black screens and I only see the status bar and my nslog but that might have to do with something else.
performSelector:withObject:afterDelay: does not wait for the selector to finish. This means that as soon as you are telling selector A to run after a certain time, it immediately goes on and tells selector B to run after a certain time. There are plenty of options to fix this:
If you would like to stick with selectors, you could use performSelector:onThread:withObject:waitUntilDone:. Make sure you don't use the main thread though or you will experience UI freezes.
[NSThread waitForInterval] is another option but, like the previous option, will freeze the UI unless you are calling the entire while loop on a different thread. I am surprised this has been mentioned so much without people noting this important factor.
GCD is another option. It doesn't wait for it to finish so you shouldn't experience major UI freezes.
dispatch_time_t dispatchTime = dispatch_time(DISPATCH_TIME_NOW, slider.value * NSEC_PER_SEC);
dispatch_after(dispatchTime, dispatch_get_main_queue(), ^(void){
[self on];
dispatch_after(dispatchTime, dispatch_get_main_queue(), ^(void){
[self off];
})
});
Another option is to keep what you are doing now and make selector B run after slider.value*2. If you think of this in a mathematically way, it makes sense:
A) 0-1-2-3-4-5-6-7-8-9-10
B) 0-1-2-3-4-5-6-7-8-9-10-1-2-3-4-5-6-7-8-9-10
This is what you're telling your app:
"Turn on something in slider.value seconds from now"
"Turn off something in slider.value seconds from now"
Do you see a problem with that?
double delayInSeconds = slider.value;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self off];
//or
[self on];
});
This performs whatever inside the block after given time! :)
The while loop doesn't wait for the delayed actions to complete, it simply schedules them. The problem is that you're scheduling all the events in a very quick loop that then runs the events one after the other just as quickly, all 10 seconds later.
[self performSelector:#selector(on) withObject:nil afterDelay:slider.value];
In this statement afterDelay doesn't mean that it will pause for some second.
It means that method "on" will execute after some second. But execution will not wait to finish execution of method"on" and go to next statement.
If you want to make only some delay than you can use following statement :
[NSThread sleepForTimeInterval:2.0];
This will wait for 2 second. But It may not be good idea because it will freeze your UI.
I hope this will help you.
All the best.....
If you want to turn it on immediately and turn it off the specified delay:
[self on];
NSLog(#"on");
[self performSelector:#selector(off) withObject:nil afterDelay:slider.value];
NSLog(#"off");
If you want to turn it on after the specified delay, then turn it off again 10 seconds after that:
[self performSelector:#selector(on) withObject:nil afterDelay:slider.value];
NSLog(#"on");
[self performSelector:#selector(off) withObject:nil afterDelay:slider.value + 10];
NSLog(#"off");
try to use [NSThread sleepForTimeInterval:1.0f]; it will stop your thread based on the time u are providing