Animation After Background Thread Complete (iOS / Swift) - ios

I'm looking to animate a few things when a long process (parsing) is complete. My barebones code currently looks like such:
func run_on_background_thread(code: () -> Void)
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), code)
}
func run_on_main_thread(code: () -> Void)
{
dispatch_async(dispatch_get_main_queue(), code)
}
func manage_response()
{
run_on_background_thread
{
long_process()
run_on_main_thread
{
UIView.animateWithDuration(animation_duration, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.8, options: UIViewAnimationOptions.CurveEaseInOut, animations:
{
some_view.transform = CGAffineTransformMakeScale(0, 0)
}, completion: nil)
}
}
}
Quite obviously, the animation does not occur. The UI just updates to the final stage. What is the proper way to thread this (I know animateWithDuration() dispatch's it's own thread, but I don't know how to hold this animation until the long process is complete.
Thanks.

I think U way can work. just change
some_view.transform = CGAffineTransformMakeScale(0, 0)
to :
some_view.transform = CGAffineTransformMakeScale(0.01, 0.01)
change the scale value close to zero; U can see the animation occur. if scale value == 0,there is no animation occur. U can try this.

Related

Expression type '#lvalue CGFloat' is ambiguous without more context

I try to make fade out animation and the code like below.
But I got the error like title, I don't know how to fix it.
Anyone who can help me ?Thx advance!!!
private func showAnimation(){
if self.resultPresent.alpha == 1.0{
UIView.animate(withDuration: 2,option: .curveEaseOut, animations: {
self.resultPresent.alpha = CGFloat(0)→Expression type '#lvalue CGFloat' is ambiguous without more context
})
}
}
Please, use following code.The parameters to the functions you are using seems to have changed
private func showAnimation()
{
if self.resultPresent.alpha == 1.0
{
UIView.animate(withDuration: 2.0, delay: 0.0, options: .curveEaseOut, animations: {
self.resultPresent.alpha = 1.0 // Final value of alpha
}, completion: nil)
}
}
There is no version of UIView.animate with the parameters you're using.
Try this instead:
UIView.animate(withDuration: 2, delay: 0, options: .curveEaseInOut, animations: {
self.resultPresent.alpha = 0
})
or, this shorter form that uses .curveEaseInOut by default:
UIView.animate(withDuration: 2, animations: {
self.resultPresent.alpha = 0
})

What does 'finish in' mean in Swift?

I'm a noob and have been learning from Apple's Playgrounds and random books doing tutorials. I'm working on a tutorial where it deals with a closure. I've seen this 'finish in' before in another tutorial but I don't know what it means precisely in layman terms.
What is it finishing, what is being finished, and inside of what? Or is there an idea of order of operation?
Here is the function where it was used:
func playSequence(index: Int, highlightTime: Double){
currentPlayer = .Computer
if index == inputs.count{
currentPlayer = .Human
return
}
var button: UIButton = buttonByColor(color: inputs[index])
var originalColor: UIColor? = button.backgroundColor
var highlightColor: UIColor = UIColor.white
UIView.animate(withDuration: highlightTime, delay: 0.0, options: [.curveLinear, .allowUserInteraction, .beginFromCurrentState], animations: {
button.backgroundColor = highlightColor
}, completion: {
finished in button.backgroundColor = originalColor
var newIndex: Int = index + 1
self.playSequence(index: newIndex, highlightTime: highlightTime)
})
}
finished is the parameter to the completion closure. The in is simply part of Swift's closure syntax.
The full signature of the UIView animate method is:
class func animate(withDuration duration: TimeInterval, delay: TimeInterval, options: UIViewAnimationOptions = [], animations: #escaping () -> Void, completion: ((Bool) -> Void)? = nil)
Note the Bool parameter to the completion closure. The finished in your code is the name given to that parameter.
An excerpt from the documentation about the completion parameter states:
This block has no return value and takes a single Boolean argument that indicates whether or not the animations actually finished before the completion handler was called.
A more typical way to write the code is as:
UIView.animate(withDuration: highlightTime, delay: 0.0, options: [.curveLinear, .allowUserInteraction, .beginFromCurrentState], animations: {
// animation code
}) { (finished) in
// completion code
}
This syntax makes it clearer than the syntax you are using. This is also using the "trailing closure" syntax.
Another way, closer to your usage, would be:
UIView.animate(withDuration: highlightTime, delay: 0.0, options: [.curveLinear, .allowUserInteraction, .beginFromCurrentState], animations: {
// animation code
}, completion: { (finished) in
// completion code
})
Your usage simply omits the parentheses around the parameter and it leaves out a line break. Adding those back in makes the code clearer.

How to user ReactiveX to execute async's in sequence

How do I leverage ReactiveX to execute async calls in sequence?
I.e., execute a second call after first one has finished.
More specifically, I'm working with RxSwift in iOS, and the asyncs I want to chain together are UIView animations (instead of calling the second animation inside the completion block of the first one).
I know I have other options like Easy Animation, but I'd like to leverage Rx, since I'm already using it for streams.
Also, one solution would be (for 3 chained animations):
_ = UIView.animate(duration: 0.2, animations: {
sender.transform = CGAffineTransformMakeScale(1.8, 1.8)
})
.flatMap({ _ in
return UIView.animate(duration: 0.2, animations: {
sender.transform = CGAffineTransformMakeScale(0.8, 0.8)
})
})
.flatMap({ _ in
return UIView.animate(duration: 0.2, animations: {
sender.transform = CGAffineTransformIdentity
})
})
.subscribeNext({ _ in })
But I'm looking for something more elegant, the right way of doing it with Rx.
I don't think using Rx makes it much cleaner, but here's how you could do it:
let animation1 = Observable<Void>.create { observer in
UIView.animateWithDuration(0.2,
animations: {
// do your animations
}, completion: { _ in
observer.onCompleted()
})
return NopDisposable.instance
}
let animation2 = Observable<Void>.create { observer in
UIView.animateWithDuration(0.2,
animations: {
// do your animations
}, completion: { _ in
observer.onCompleted()
})
return NopDisposable.instance
}
Observable.of(animation1, animation2)
.concat()
.subscribe()
.addDisposableTo(disposeBag)
It would also be cleaner if you create a function to construct the Observable<Void>s for you.
func animation(duration: NSTimeInterval, animations: () -> Void) -> Observable<Void> {
return Observable<Void>.create { observer in
UIView.animateWithDuration(duration,
animations: animations,
completion: { _ in
observer.onCompleted()
})
return NopDisposable.instance
}
I guess a plus side to using Rx instead of just animateWithDuration:animations: chained, is that you don't have to nest the animations in completion blocks. This way, you can just define them on their own and compose them as you want afterward.
As an alternative to RxSwift, check out PromiseKit. RxSwift is a bit overkill for your animation callback needs. This blog post in particular is relevant.
Maybe its too late for #Rodrigo Ruiz, but I'm sure it might help other developers that happen to come across this post (like I did).
RxAnimated is available for free on GitHub: https://github.com/RxSwiftCommunity/RxAnimated
You can get started using this blog post
A small discloser - I'm not connected to this project or post by any way - I just found it while searching for a reactive solution for my animations :)

Loop Random Number Generation Without Crashing Application Swift 2.2

Currently I am writing a looping animation method. This method generates a new arc4random_uniform and then converts it to a CGFloat. These values are then inserted into the animateWithDuration function as so:
func randomAnimationForPostPackets() {
while true {
let randomCoordinatesXInt: UInt32 = arc4random_uniform(300)
let randCoordsX = CGFloat(randomCoordinatesXInt)
let randomCoordinatesYInt: UInt32 = arc4random_uniform(300)
let randCoordsY = CGFloat(randomCoordinatesYInt)
UIView.animateWithDuration(5, delay: 0, options: [.AllowUserInteraction], animations: {
self.postPacketView.center.x = randCoordsX
self.postPacketView.center.y = randCoordsY
}, completion: nil)
}
}
When attempting to loop this function in a while loop, the application crashes due to the constant generation of random numbers. How would I be able to implement a looped animation based on a new set of random coordinates each time?
Using a while loop will just call animations as fast as it can using all the CPU and you will eventually run out of memory.
animateWithDuration has a completion closure that is called when the animation is complete. Use this to set up the next animation.
func randomAnimationForPostPackets() {
let randomCoordinatesXInt: UInt32 = arc4random_uniform(300)
let randCoordsX = CGFloat(randomCoordinatesXInt)
let randomCoordinatesYInt: UInt32 = arc4random_uniform(300)
let randCoordsY = CGFloat(randomCoordinatesYInt)
UIView.animateWithDuration(5, delay: 0, options: [.AllowUserInteraction], animations: {
self.postPacketView.center.x = randCoordsX
self.postPacketView.center.y = randCoordsY
}) { (finished) in
self.randomAnimationsPostPacket()
}
}
#Paulw11 basically nailed it in the comments: Your call to animateWithDuration is async, i.e. it isn't "blocking" your loop for 5 seconds. Your loop is spinning wicked fast, like, a bajillion times before that first animate even gets a chance. In fact, I suspect your loop is taking ALL the resources such that the animate never even tries.

UIView.animateWithDuration in Swift 2.0?

Before explain my problem, it is important to say that I already implemented the suggestion made in this question and I think my doubts about this animateWithDuration method are quite different, despite both questions having a very similar title.
So, I am a Swift newbie and I am doing some small projects in Swift, based on previous Objective C demos that I did before.
This is my Objective C code:
- (void)moveSideBarToXposition: (int) iXposition{
[UIView animateWithDuration:0.5f
delay:0.1
options: UIViewAnimationOptionTransitionNone
animations:^{ self.mainView.frame = CGRectMake(iXposition, 20, self.mainView.frame.size.width, self.mainView.frame.size.height); }
completion:^(BOOL finished){
if (self.isSidebarHidden==YES) {
self.isSidebarHidden = NO;
}
else{
self.isSidebarHidden = YES;
}
}];
}
And this is my Swift version:
func moveSideBarToXposition(iXposition: Float) {
UIView.animateWithDuration(0.5, delay: 1.0, options: UIViewAnimationTransition.None, animations: { () -> Void in
self.contentView.frame = CGRectMake(iXposition, 20, self.contentView.frame.size.width, self.contentView.frame.size.height)
}, completion: { (finished: Bool) -> Void in
if isMenuHidden == true {
isMenuHidden = false
} else {
isMenuHidden = true
}
})
}
And I get this error.
Cannot invoke 'animateWithDuration' with an argument list of type
'(Double, delay: Double, options: UIViewAnimationTransition,
animations: () -> Void, completion: (Bool) -> Void)'
I read the documentation but actually I am not sure what the problem is.
Btw, i am working on Xcode 7 and Swift 2.0
You are passing enum of type UIViewAnimationTransition to an argument which requires type UIViewAnimationOptions (options argument)
Here is the correct syntax with the correct enum value:
func moveSideBarToXposition(iXposition: Float) {
let convertedXposition = CGFloat(iXposition)
UIView.animateWithDuration(0.5, delay: 1.0, options: UIViewAnimationOptions.TransitionNone, animations: { () -> Void in
self.contentView.frame = CGRectMake(convertedXposition, 20, self.contentView.frame.size.width, self.contentView.frame.size.height)
}, completion: { (finished: Bool) -> Void in
// you can do this in a shorter, more concise way by setting the value to its opposite, NOT value
isMenuHidden = !isMenuHidden
})
}

Resources