UIProgressView has this setProgress:animated: API.
Is there a way to know exactly when the animation stops?
I mean something like this?
[myProgress setProgress:0.8f animated:YES onCompletion...]
I would like to start fading the progress out, as soon as its animation ends.
From: https://stackoverflow.com/a/16368679/74815
When you are not the author of the animation, you can get a callback when the animation ends by using a transaction completion block:
[CATransaction setCompletionBlock:^{
// doSomethingElse
}];
// doSomething
From the Apple documentation:
Discussion
The completion block object that is guaranteed to be called (on the main thread) as soon as all animations subsequently added by this transaction group have completed (or have been removed.) If no animations are added before the current transaction group is committed (or the completion block is set to a different value,) the block will be invoked immediately.
Related
I have an OperationQ and for loop inside this. inside this for loop Im calling dispatch_after the delay is growing whenever the for loop iteration happen. this is to make sure the thing happen inside for loop will happen with a delay and sequentially.
Now when I want to reset this OperationQ with new data. Im cancelling all operations of the operationQ. this cancel the operation but the dispatch after thrown with a dispatch timer still running. is there a w ay I can cancel the dispatch_after inside the operationQ?
OperationQ addoperationWithBlock({
mainQ addOperationwithBlock({
// Do some UI work
delayCOnstant
for (iteration)
{
delay += delayCOnstant
dipathTime = dispatch_timeNow + delay
dispatch_after(time, mainQ,
{
// UI Drawing, dispatch after will draw smoothly and sequence
})
}
})
})
When i cancel the OperationQ the operation stops but the dispatch_after is still running with the dispatch_time we have given. Any ideas or suggestions
dispatch_after has no cancel mechanism. Being able to cancel is one of the advantages of NSOperationQueue. So instead of doing the work directly in your dispatch_after block, use that block to schedule an NSOperation which can then also be cancelled.
As Curmudgeonlybumbly mentioned I used the NSOperation. but the thing i missed was once the operation started cancel was not doing anything. I have an iteration inside the operation. so i checked for self.cancelled and return. that worked. Thanks
I'm calling a method which belongs to custom delegate class on viewDidLoad but it starts from [sampleProtocol startSampleProcess], starts from sleep(5), before it show me view controller and label1.
CustomDelegate *sampleProtocol = [[CustomDelegate alloc]init];
sampleProtocol.delegate = self;
[self.label1 setText:#"Processing..."];
[sampleProtocol startSampleProcess];
startSampleProcess method is below;
-(void)startSampleProcess{
sleep(5);
[self.delegate processCompleted];
}
processCompleted method is also below;
-(void)processCompleted{
[self.label1 setText:#"Process Completed"];
}
It just set a label on viewcontroller, go to another class and do something simple (etc: sleep) and come back to view controller and set label again. I didn't try custom delegate before so it would be great if you help me on what I'm missing.
The problem is that you are calling sleep on the main thread.
Here's how an iOS app works:
Wait until something interesting happens.
Process it.
Go back to step 1.
The app has something called a runloop that receives messages from the system about touches, timers, etc. Every time it gets a message, it runs some code, often provided by you. When you call the sleep function, it suspends the current thread. When the thread is suspended, the run loop can't process new events until the sleep is done.
When you change something onscreen, you add an event to the run loop that says the screen needs to be redrawn. So, this is what is happening in your application:
You change the label text. A redraw event is now added to the runloop.
You sleep for 5 seconds, meaning the runloop can't process new events.
5 seconds later, the thread wakes up and changes the label's text.
Control finally gets back to the run loop.
The run loop processes the redraw event, changing the label's text.
If the task needs to be a long-running task, you can do it in a background thread:
-(void)startSampleProcess {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_NORMAL, 0) ^{ //run this code in the background so it doesn't block the runloop
sleep(5);
dispatch_async(dispatch_get_main_thread(), ^{ //after the task is done, call the delegate function back on the main thread since UI updates need to be done on the main thread
[self.delegate processCompleted];
});
});
}
I am trying to get a better understanding of this topic. Lets say I want to do some really cool animation like the following
- (void)coolAnimation
{
[UIView animateWithDuration: some duration
animations:^{ some animation }];
}
Since its an animation block, does it automatically added to the main_queue ? Or for best practice, I should always add the UI updates into the main_queue like the following.
dispatch_async(dispatch_get_main_queue(), ^{
[self coolAnimation];
});
The contents of your block are performed on the main thread regardless of where you call [UIView animateWithDuration:animations:]. It's best to let the OS run your animations; the animation thread does not block the main thread -- only the animation block itself.
In a very high-level hand-wavy kind of view of this, [UIView animateWithDuration:animations:] evaluates your block and determines functions for each animatable value within it. The OS has an animation thread that it uses as its frame timer, then at each tick it sends an update on the main thread to your animatable properties with their progressing values. (You can check the current state of these values on your presentationLayer.)
The animation block you pass is called immediately. No need to use any queues.
I am working with Core Animation using CATransaction. I am using setCompletionBlock in order to capture when the animation is complete so that I can do stuff with the data in the view controller, but I want the animation to be interruptible. Eg. when I call
[self.layer removeAllAnimations]
the animation should stop but the setCompletionBlock should also KNOW if the animation succeeded or failed.
With UIView animation, this is possible since there is a finished variable passed in the completion block and with CAAnimationGroup this is also possible with a finished variable passed to the delegate. How do I accomplish the same with a CATransaction?
Using key-value coding, you can pass any variable you like into the current transaction where it can be picked up by the completion block later. CATransaction, CAAnimation, CALayer, they all accept arbitrary key-value pairs which you can create and use to your heart's content.
https://developer.apple.com/library/ios/documentation/graphicsimaging/Reference/CATransaction_class/Introduction/Introduction.html#//apple_ref/occ/clm/CATransaction/setValue:forKey:
I have a seemingly simple problem that I cannot for the life of me seem to figure out. In my iOS App, I have a UICollectionView that triggers network operation upon tapping it that can take a few seconds to complete. While the information is being downloaded, I want to display a UIView that fills the cell with a UIActivityIndicatorView that sits in the square until the loading is done, and the segue triggered. The problem is that it never appears. Right now my code looks like:
myLoadView.hidden = NO;
//Network Operation
myLoadView.hidden = YES;
The App simply stops for a couple seconds, and then moves on the the next view. I'd imagine Grand Central Dispatch has somthing to do with the solution, however please keep in mind that this code takes place in prepareForSegue, and the network info needs to be passed to the next View. For this reason not finishing the download before switching scenes has an obvious problem. Any help would be VASTLY appreciated. Thanks!
iOS commits changes in the interfaces after working out a routine. Hence you should perform your network operation in a background thread and then get back back on the main and perform the "show my view now thing". Have a look the below code for reference.
myLoadView.hidden = NO;
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
//Network Operation
dispatch_async(dispatch_get_main_queue(), ^{
myLoadView.hidden = YES;
});
});
Your network operation seems to be carried out on the main thread, aka UI thread. This blocks all further UI calls, including the call to unhide a view, until completion.
To resolve this, make your call asynchronous.
You should read this in full, if you haven't already.
As mentioned by other answers, the problem is that the UIView change doesn't happen until the current method finishes running, which is where you are blocking. Before GCD was available I would split methods in two and use performSelector:withObject:afterDelay (to run the second part also on the UI loop) or performSelectorInBackground:withObject: at the end of the first method. This would commit all the waiting animaations first, then do the actual tasks in the second method.
Well the better option for this type of indication is by using the custom HUD libraries like SVProgressHUD or MBProgressHUD