Images animation with completion block? - ios

Using Swift , I animate multiple images with :
cp.animationImages = images
cp.animationDuration = TimeInterval(speed)
cp.animationRepeatCount=count
cp.startAnimating()
I use all sorts of delays to wait between them, but when I need to run a sequence of animations, I would like to have a delegate so each call will be finished with a certain tag, so I can then decide what to do.
I could not find how to use a block/delegate in Swift for this animation.

UIImageView does not support animation completion handlers, but there are extensions available:
https://github.com/gurmundi7/UIImageView-AnimationCompletionBlock

Related

Combine multiple MTKView/CAMetalLayer display

I am using multiple MTKViews to display different contents on the screen along with normal UIView's (for displaying UI). I want to synchronize presentation of these MTKViews together with the same clock. Is there a way to synchronize the presentation of these MTKViews? In principal, I can combine the layouts of these views to a single MTKView but that would kill modularity of the code and not sure if I would achieve anything on the performance with so much of overwork.
A simple approach that should work in most cases would be to compute the time at which you'd like the frame to draw and use the present(_:atTime:) method of MTLCommandBuffer instead of the present(_:) method.
To exert greater control, it helps to understand what a command buffer's present... methods do and don't do. They do not encode any command into the buffer. As documented, they basically just add a scheduled handler to themselves that calls present on the drawable.
If you're careful about it, you can arrange to present the drawable in a way that doesn't much involve the command buffer.
But, does the command buffer using a scheduled handler make sense? Shouldn't it use a completed handler? After all, you want to display the completed rendering, right?
Well, drawables are smart about presenting themselves. The present method doesn't present immediately. A drawable tracks which scheduled commands might render or write to its texture. When present is called, that arranges for the drawable to draw itself on the screen as soon as possible after all such commands have completed. (Note that this does not imply that the command buffer itself has completed. There may be additional commands that don't involve the drawable's texture that aren't yet completed.)
This provides both challenges and opportunities for syncing the presentation of multiple drawables. The challenge is that, while you can control when you call present on each drawable, that doesn't necessarily sync their actual display, because each will display as soon it can after present is called and all commands involving its texture are completed, and that last part can occur at different times for different drawables.
One possible approach to solving this is to add a presented handler to the master drawable. The handler would call present on the other 3 drawables. After all of the command buffers are scheduled, call present on the master drawable. You can use a dispatch group to determine when all of the command buffers are scheduled. Enter the group once for each command buffer and add a scheduled handler to each that leaves the group. Then set a notify block on the group that does the master present. This technique probably won't achieve perfect synchronization because there's latency between when the master drawable has actually presented and when the presented handler is called, and then latency in presenting the other drawables.
Another possible approach is to set the presentsWithTransaction property of all of your CAMetalLayers to true. Then, when it's time to present, call waitUntilScheduled on each command buffer followed by present on each drawable. (Do not use a present... method of the command buffer.) This will guarantee that all of the drawables will present during the same Core Animation transaction – that is, synchronized.
You can use presentsWithTransaction
Set presentsWithTransaction = true in MTKView
Change commandBuffer commit style
public func draw(in view: MTKView) {
...
commandBuffer?.commit()
commandBuffer?.waitUntilScheduled()
view.currentDrawable?.present() }
Now all the metal views will be displayed synchronously.
It works for me.

UIViewPropertyAnimator - simply stop all animations, rather than a specific one?

In UIViewPropertyAnimator, is there a way to just stop all UIViewPropertyAnimator animations?
Or perhaps simply get all current animations - then of course you could stop them all.
Can this be done?
Or do you really have to
(a) do only one per UIViewPropertyAnimator,
and,
(b) keep a reference to each of those?
Every animation has to have atleast one UIViewPropertyAnimator instance. In order to stop the animation, we have to explicitly call stopAnimation(_:) on the animator instance.
A way would be to make a factory class to fetch UIViewPropertyAnimator instance and keep track of it in a set or array. And then use this factory class to stop or start all the animations at once. Or use one UIViewPropertyAnimator to perform all your animations and stop it.

Update UIView before calling other function?

I am working in Swift. When a user presses a UIButton it calls a function ButtonPressed(). I would like ButtonPressed() to do two things:
Update the UIView by removing the current buttons and texts, then uploading some new text.
Call function TimeConsumingCalculation(). TimeConsumingCalculation is the complicated part of my app and does some calculations which take about 20 seconds or so to complete.
Right now, I have the code in the basic order:
ButtonPressed(){
self.Button.removeFromSuperview()
TimeConsumingCalculation()
}
However, it will not remove the button or do any other UI updates or additions until after the TimeConsumingCalculation is complete. I have read and attempted a few guides on closures and asynchronous functions, but have had no luck. Is there a special property with UIView that is causing it to be updated last?
As a side note - I have already attempted putting all UI actions in a separate function and calling it first. It doesn't work. The time consuming function does not take any variables from the buttons or UI or anything like that.
Thanks!
It seems like timeConsumingCalculation() is blocking the main queue, which is in charge of UI updates. Try calling it like this instead and use the isHidden property to hide the button instead of removing it from the view completely.
ButtonPressed(){
self.Button.isHidden = true
DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated).async {
self.timeConsumingCalculation()
}
}
here you call timeConsumingCalculation() asynchronously on a background thread. The quality of service we give it is userInitiated, read more about quality of service classes here

CATransaction complete block success or failure

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:

How do iOS animation blocks work?

In iOS, you can animate view objects using animation blocks:
[UIView animateWithDuration:1.0 animations:^{
firstView.alpha = 0.0;
secondView.alpha = 1.0;
}];
What we have here is a code block that describes what the view properties will end up looking after the animation is finished.
How does this work?
I could understand (I think) if this was done using some declarative format, but from the looks of it, the animation block is just a regular piece of code that presumably has to be executed, the results inspected and then someone transcoded into the actual lower-level graphics code that performs the animation.
Is the block actually executed (or somehow reverse-engineered) and if so, when?
If this code is executed before the animation starts, then how come the changes to the referenced view properties are not reflected immediately?
What happens if I put code in the block that does not change view properties, but does something else?
Yes, the block is actually invoked -- then it changes the view's properties immediately. The UIView's property setters are responsible to see if the set was used within an animation context -- if so, they calculate the animation frames etc. using CoreAnimation and CoreGraphics.
If you put non-animation code into these blocks, nothing special will happen -- the block will be executed immediately.
It is instructive to look at the equivalent code prior to blocks:
[UIView beginAnimations:#"foo" context:NULL];
[UIView setAnimationDuration:1.0];
firstView.alpha = 0.0;
secondView.alpha = 1.0;
[UIView commitAnimations];
So you see, even before blocks, the properties to change are also set directly; however, they do not take effect immediately (they are animated).
How does it work? Presumably when you set a property on the view, it checks to see if you're run beginAnimations but not commitAnimations, and does not take effect immediately if it is (but rather adds it to the list of things to animate for that animation).
So what the blocks version does is very simple in the context of the pre-blocks version: you can just think of it as running the animation block inside beginAnimations and commitAnimations lines.
Apple doesn't really talk about the nitty-gritty details of how it works, but here's what I think happens:
The system adds KVO observers on all the animatable properties of a view when the view is added to the view hierarchy.
When your animation block executes, the system sets a state that watches for KVO notifications on those properties. The code that gets invoked then creates and adds the appropriate CAAnimation objects to each affected view's layer.

Resources