SITUTATION:
I have created a pulsating animation and it works.
Using the repeatForever property, I was able to make it continuous as I wanted.
But one issue remains that I wasn't able to fix after doing A LOT of research.
EXPLANATION:
The only problem is that I don't know how to add one more animation that makes the Image go back to it's original size before pulsating again.
The challenge for me resides in the fact that if I declare it after the other one, it will not be called as it will not be included in the repeatForeverloop.
Therefore, the animation I have right now is not smooth as once it completes, the image instantly goes back to it's original size before repeating itself.
WHAT HAPPENS:
1) Image pulsates to 1.2 of current size thx to animation.
THIS IS NOT SMOOTH:
2) Animation finishes at 1.2 and Image instantly "warps" back to 1.0
3) Animation repeats itself. Image pulsates to 1.2 again.
PROBLEM: How can I change my pulsatingAnimation so that my image goes back to it's original size in a smooth way before pulsating again ?
CODE:
import Foundation
import pop
class AnimationEngine {
func pulsate(object: UIImageView) {
let pulsateAnim = POPSpringAnimation(propertyNamed: kPOPLayerScaleXY)
pulsateAnim.velocity = NSValue(CGSize: CGSizeMake(0.1, 0.1))
pulsateAnim.toValue = NSValue(CGSize: CGSizeMake(1.2, 1.2))
pulsateAnim.springBounciness = 18
pulsateAnim.dynamicsFriction = 20
pulsateAnim.springSpeed = 1.0
pulsateAnim.repeatForever = true
// pulsateAnim.autoreverses = true
object.layer.pop_addAnimation(pulsateAnim, forKey: "layerScaleSpringAnimation")
}
}
I found the Solution.
SOLUTION:
func pulsate(object: UIImageView) {
let pulsateAnim = POPSpringAnimation(propertyNamed: kPOPLayerScaleXY)
let returnToSizeAnim = POPBasicAnimation(propertyNamed: kPOPLayerScaleXY)
object.layer.pop_addAnimation(pulsateAnim, forKey: "layerScaleSpringAnimation")
pulsateAnim.velocity = NSValue(CGSize: CGSizeMake(0.1, 0.1))
pulsateAnim.toValue = NSValue(CGSize: CGSizeMake(1.2, 1.2))
pulsateAnim.springBounciness = 30
pulsateAnim.dynamicsFriction = 20
pulsateAnim.springSpeed = 1.0
pulsateAnim.completionBlock = {(animation, finished) in
object.layer.pop_addAnimation(returnToSizeAnim, forKey: "layerScaleSpringAnimation")
}
returnToSizeAnim.toValue = NSValue(CGSize: CGSizeMake(1.0, 1.0))
returnToSizeAnim.duration = 0.5
returnToSizeAnim.completionBlock = {(animation, finished) in
object.layer.pop_addAnimation(pulsateAnim, forKey: "layerScaleSpringAnimation")
}
}
func pulsate2(object: UIImageView) {
let pulsateAnim = POPSpringAnimation(propertyNamed: kPOPLayerScaleXY)
let returnToSizeAnim = POPBasicAnimation(propertyNamed: kPOPLayerScaleXY)
object.layer.pop_addAnimation(pulsateAnim, forKey: "layerScaleSpringAnimation")
pulsateAnim.velocity = NSValue(CGSize: CGSizeMake(0.1, 0.1))
pulsateAnim.toValue = NSValue(CGSize: CGSizeMake(1.2, 1.2))
pulsateAnim.springBounciness = 30
pulsateAnim.dynamicsFriction = 20
pulsateAnim.springSpeed = 1.0
pulsateAnim.completionBlock = {(animation, finished) in
object.layer.pop_addAnimation(returnToSizeAnim, forKey: "layerScaleSpringAnimation")
}
returnToSizeAnim.toValue = NSValue(CGSize: CGSizeMake(1.0, 1.0))
returnToSizeAnim.duration = 0.5
returnToSizeAnim.completionBlock = {(animation, finished) in
object.layer.pop_addAnimation(pulsateAnim, forKey: "layerScaleSpringAnimation")
}
}
N.B.:
I need to declare one pulsate function for each object I want to use it on. If I don't do that, the second time I call the function, it won't work properly because an instance of the animations is already running.
I'm a huge ReactiveCocoa nerd so I built a class around POPAnimation so I could deal with animations in a more functional manner.
import Foundation
import ReactiveCocoa
import pop
import Result
/**
`UIView` wrapper for performing reactive-based pop animations.
*/
public struct ReactivePOPView<View: UIView> {
/// View object
private weak var view: View?
/// `SignalProducer` object when the wrapper is about to deallocate.
private let willDealloc: SignalProducer<Void, NoError>
/// Wrapper on the view property.
private var animator: View? { return self.view }
/**
Create an animation wrapper with a view.
- parameter view: `UIView` object.
*/
public init(_ view: View) {
self.view = view
self.willDealloc = view
.willDeallocSignalProducer()
}
}
/**
Binds a `Signal` that emits an animation object to a `ReactivePOPView`.
- parameter view: `ReactivePOPView` object.
- parameter signal: `Signal` object.
- returns: `Disposable` object.
*/
public func <~ <T, Animation: POPAnimation>(view: ReactivePOPView<T>, signal: Signal<Animation, NoError>) -> Disposable {
let disposable = CompositeDisposable()
let viewDisposable = view.willDealloc.startWithCompleted {
disposable.dispose()
}
disposable.addDisposable(viewDisposable)
let signalDisposable = signal.observe(Observer(next: {
view.animator?.pop_addAnimation($0, forKey: $0.name ?? "")
}, completed: {
disposable.dispose()
}))
disposable.addDisposable(signalDisposable)
return disposable
}
/**
Binds a `SignalProducer` that emits an animation object to a `ReactivePOPView`.
- parameter view: `ReactivePOPView` object.
- parameter producer: `SignalProducer` object.
- returns: `Disposable` object.
*/
public func <~ <T, Animation: POPAnimation>(view: ReactivePOPView<T>, producer: SignalProducer<Animation, NoError>) -> Disposable {
var disposable: Disposable!
producer.startWithSignal { signal, signalDisposable in
view <~ signal
disposable = signalDisposable
view.willDealloc.startWithCompleted {
signalDisposable.dispose()
}
}
return disposable
}
/**
Bind a reactive animation property to a `ReactivePOPView` property.
- parameter destinationProperty: `ReactivePOPView` property.
- parameter sourceProperty: Animation property.
- returns: `Disposable` object.
*/
public func <~ <T, P: PropertyType where P.Value == POPAnimation>(destinationProperty: ReactivePOPView<T>, sourceProperty: P) -> Disposable {
return destinationProperty <~ sourceProperty.producer
}
With this class, you can do things like:
ReactivePOPView<UIButton>(self.loginButton) <~ self.someSignal.signal
.flatMap(.Concat) { _ in SignalProducer<POPBasicAnimation, NoError> in
let animation ...
// construct animation here
return SignalProducer(value: animation)
}
.repeat(0)
I also have a wrapper class for constraints if you want that.
I dont know Facebook pop , so I speak only about the analysis and logic to how to implement this feature (pulse effect)
As I've write in comments, what you want to do seems exactly what happened many times in SpriteKit where you , to make a specific animation (SKAction), have to build a series of actions.
Just to make a SpriteKit example, this is what I do to make a specific pulse effect:
let titleForward = runTitleForward()
let wait = SKAction.waitForDuration(5.0)
let titleBackward = runTitleBackward()
let titleAnim = SKAction.repeatActionForever((SKAction.sequence([titleForward, wait, titleBackward,wait ])))
title.runAction(titleAnim, withKey:"titleAnimation")
func runTitleForward()-> SKAction {
let atlascTexturesArray = self.myManager.atlascTexturesDictionary["title-anim"]
let texturesAction = SKAction.animateWithTextures(atlascTexturesArray!,timePerFrame: 0.09)
return texturesAction
}
func runTitleBackward()-> SKAction {
let texturesAction = runTitleForward()
let texturesReverseAction = SKAction.reversedAction(texturesAction)
return texturesReverseAction()
}
Hope you help to do the same thing in UIKit.
UPDATE: (I don't test it..)
func delayAnimation(duration:NSTimeInterval)-> CABasicAnimation {
let animation : CABasicAnimation = CABasicAnimation(keyPath: "opacity");
animation.delegate = self
animation.fromValue = 1
animation.toValue = 1 // fake animation, just to obtain duration delay..
animation.duration = duration
return animation
}
func pulseElements () -> (POPSpringAnimation,POPSpringAnimation) {
// zoom in
let pulsateInAnim = POPSpringAnimation(propertyNamed: kPOPLayerScaleXY)
pulsateInAnim.velocity = NSValue(CGSize: CGSizeMake(0.1, 0.1))
pulsateInAnim.toValue = NSValue(CGSize: CGSizeMake(1.2, 1.2))
pulsateInAnim.springBounciness = 18
pulsateInAnim.dynamicsFriction = 20
pulsateInAnim.springSpeed = 1.0
// zoom out
let pulsateOutAnim = POPSpringAnimation(propertyNamed: kPOPLayerScaleXY)
...
return (pulsateInAnim,pulsateOutAnim)
}
func pulseAnimation() {
let (pulsateInAnim,pulsateOutAnim) = pulseElements()
let wait = delayAnimation(1.0)
var pulseAnimationGroup: CAAnimationGroup = CAAnimationGroup()
pulseAnimationGroup.animations = [pulsateInAnim, wait, pulsateOutAnim, wait]
pulseAnimationGroup.duration = 0.5
pulseAnimationGroup.repeatCount = Float.infinity
object.layer.addAnimation(pulseAnimationGroup, forKey: layerScaleSpringAnimation)
}
Related
In Swift 4 i am loaded/holding a set of animations from dae files in an array:
var animations = [String: CAAnimation]()
loadAnimation(withKey: "stepL", sceneName: "art.scnassets/StepL", animationIdentifier: "StepL-1")
loadAnimation(withKey: "stepR", sceneName: "art.scnassets/StepR", animationIdentifier: "StepR-1")
func loadAnimation(withKey: String, sceneName:String, animationIdentifier:String) {
let sceneURL = Bundle.main.url(forResource: sceneName, withExtension: "dae")
let sceneSource = SCNSceneSource(url: sceneURL!, options: nil)
if let animationObject = sceneSource?.entryWithIdentifier(animationIdentifier, withClass: CAAnimation.self) {
// The animation will only play once
animationObject.repeatCount = 1
// To create smooth transitions between animations
animationObject.fadeInDuration = CGFloat(0.8)
animationObject.fadeOutDuration = CGFloat(0.6)
// Store the animation for later use
animations[withKey] = animationObject
}
}
Later on i am playing the animation by adding it to my scene:
func playAnimation(key: String) {
print("fire animation: " + key)
scnView.scene?.rootNode.addAnimation(animations[key]!, forKey: key)
}
This seems to work when doing them individually from touches, but i want to be able to trigger a sequence of these with very specific timing.
I've tried building a loop and sending them off to a DispatchQueue with specific timing outlined:
var delaytime = state.steptime!
for _ in 0..<state.trCount! {
for wSide in state.walkSeq! {
walkQ.asyncAfter(deadline: .now() + delaytime){
self.playAnimation(key: wSide)
}
delaytime += state.steptime! + 1.0
}
}
And i've tried a similar, but slightly different approach using wallDeadline:
let wt = DispatchWallTime.now() + state.pausetime!
//build sequence
var train_seq = [(name:"stepL", time:wt), (name:"stepR",time:wt + state.pausetime! + state.steptime!)]
for _ in 0..<state.trCount! {
let lasttime = train_seq[ train_seq.count - 1 ].time
train_seq += [(name:"stepL", time:lasttime + state.steptime!)]
train_seq += [(name:"stepR", time:lasttime + state.pausetime! + state.steptime!)]
}
//send it
for i in 0..<train_seq.count {
walkQ.asyncAfter(wallDeadline: train_seq[i].time){
self.playAnimation(key: train_seq[i].name)
}
}
What inevitably happens is that the first few run as expected, then around the 4th set of sequences they start to stack on top of each other and fire at the same time, no longer adhering to the timing that i've defined
What is the best way to trigger animations in a sequence on an expected timeline? Is there a way to pause/sleep a loop until the previous animation stops, then trigger the next in the series?
Thanks!
i ended up making a sequence of SCNActions in the order i needed, including a wait
var seq = [SCNAction]()
for _ in 0..<state.trCount!{
let act1 = SCNAction.run {_ in
self.playAnimation(key: "stepL")
}
seq.append(act1)
let act2 = SCNAction.wait(duration: state.steptime!)
seq.append(act2)
let act3 = SCNAction.run { _ in
self.playAnimation(key: "stepR")
}
seq.append(act3)
seq.append(act2)
let act4 = SCNAction.run { _ in
self.playAnimation(key: "idle")
}
seq.append(act4)
let act5 = SCNAction.wait(duration: state.pausetime!)
seq.append(act5)
}
let sequence = SCNAction.sequence(seq)
scnView.scene?.rootNode.runAction(sequence, completionHandler:nil )
I am calling users "Weight" from firebase database in a label (which is stored under 'profile/uid') which has two categories "kg" and "lbs". I would like the label to be able to show user's weight in kg and then fade out to lbs and then back to kg again so on and so fourth. So far i have written the following code which is constantly fading in and out the weight in lbs and i'm unsure how to get it to display the weight in kg once lbs fades out:
func weight() {
let userRf = self.firDatabaseRef.child("profile").child("\(User!.uid)").observe(FIRDataEventType.value, with: { (snapshot) in
if snapshot.exists() {
if let weight = snapshot.childSnapshot(forPath: "weight").value as? [String: AnyObject] {
if let kg = weight["kg"] as? Float, let lbs = weight["lbs"] as? Int {
let animation: CATransition = CATransition()
animation.duration = 2.0
animation.type = kCATransitionFade
animation.repeatDuration = .infinity
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
self.displayWeight.layer.add(animation, forKey: "changeTextTransition")
self.displayWeight.text = "\(lbs) lbs"
}
}
}
}
}
One way is to use a repeating UIVIew animation along with a Timer:
// set up some helpful constants
let kgsText = "\(kgs) kgs"
let lbsText = "\(lbs) lbs"
let fadeDuration = 2.0
// keep track of which unit is being displayed
var isDisplayingKgs = true
// animate the fade in and out, repeating
UIView.animate(withDuration: fadeDuration, delay: 0, options: [.autoreverse, .repeat], animations: {
label.alpha = 0
}, completion: nil)
// start the timer to switch the text after a short delay
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + fadeDuration) {
self.displayWeight.text = lbsText
isDisplayingKgs = false
Timer.scheduledTimer(withTimeInterval: fadeDuration * 2, repeats: true, block: { timer in
self.displayWeight.text = isDisplayingKgs ? lbsText : kgsText
isDisplayingKgs = !isDisplayingKgs
})
}
This is assuming that your label starts out visible (alpha = 1). The dispatch delay is to ensure that the first transition occurs when the label is faded out the first time, but before the timer kicks in.
Here is what the animation looks like in action:
I am trying to create an animation where two line are faded out when the user drags a UIView and faded back in when user releases dragging.
Therefore I have two functions undrawLines (called at pan gesture start) and redrawLines (called at pan gesture end) which are called by my UIPanGestureRecognizer action handler.
func undrawLines() {
line1.opacity = 0.0
line2.opacity = 0.0
line1.removeAllAnimations()
line2.removeAllAnimations()
let opacityLine = CABasicAnimation(keyPath: "opacity")
opacityLine.fromValue = 1.0
opacityLine.toValue = 0.0
opacityLine.duration = 0.15
line1.add(opacityLine, forKey: "disappearLine1")
line2.add(opacityLine, forKey: "disappearLine2")
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: {
mill.line1.removeFromSuperlayer()
mill.line2.removeFromSuperlayer()
})
}
func redrawLines() {
line1.opacity = 1.0
line2.opacity = 1.0
print("redraw")
line1.removeAllAnimations()
line2.removeAllAnimations()
self.layer.addSublayer(line1)
self.layer.addSublayer(line2)
let opacityLine = CABasicAnimation(keyPath: "opacity")
opacityLine.fromValue = 0.0
opacityLine.toValue = 1.0
opacityLine.duration = 0.15
line1.add(opacityMill, forKey: "appearLine1")
line2.add(opacityMill, forKey: "appearLine2")
}
The problem is that when redrawLines gets called while the undrawLines animation is still running, the lines show a strange behavior and opacity is 0.
Here is a demo, the first part shows how it should be, the second one shows the bug:
I believe your issue here is a race condition with your completion handler:
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: {
mill.line1.removeFromSuperlayer()
mill.line2.removeFromSuperlayer()
})
If your user releases, and therefore redrawLines gets called before the 0.3 second timeout, this still gets called and removes the lines.
You probably want to keep a state flag that indicates the current intent and then check it in the asynchronous callback:
func undrawLines() {
self.linesHidden = true // update state
line1.opacity = 0.0
line2.opacity = 0.0
line1.removeAllAnimations()
line2.removeAllAnimations()
let opacityLine = CABasicAnimation(keyPath: "opacity")
opacityLine.fromValue = 1.0
opacityLine.toValue = 0.0
opacityLine.duration = 0.15
line1.add(opacityLine, forKey: "disappearLine1")
line2.add(opacityLine, forKey: "disappearLine2")
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: { [weak self] in
if self?.linesHidden == true { // check this is still what we want to do
mill.line1.removeFromSuperlayer()
mill.line2.removeFromSuperlayer()
}
})
}
func redrawLines() {
self.linesHidden = false // update state
line1.opacity = 1.0
line2.opacity = 1.0
print("redraw")
line1.removeAllAnimations()
line2.removeAllAnimations()
self.layer.addSublayer(line1)
self.layer.addSublayer(line2)
let opacityLine = CABasicAnimation(keyPath: "opacity")
opacityLine.fromValue = 0.0
opacityLine.toValue = 1.0
opacityLine.duration = 0.15
line1.add(opacityMill, forKey: "appearLine1")
line2.add(opacityMill, forKey: "appearLine2")
}
You'll clearly need to add the instance var linesHidden to the class for this to work too :)
I have a image that I would like to keep spinning if a button is pressed, until I call an action to stop the spinning. I tried these websites: https://www.andrewcbancroft.com/2014/10/15/rotate-animation-in-swift/ I had to made chances to the delegation, it crashes when I try to changed it
and https://bencoding.com/2015/07/27/spinning-uiimageview-using-swift/ just does not spin when I call the action. Thank you. Code is here below:
Andrewcbancroft.com:
extension UIView {
func rotate360Degrees(duration: CFTimeInterval = 1.0, completionDelegate: AnyObject? = nil) {
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
rotateAnimation.fromValue = 0.0
rotateAnimation.toValue = CGFloat(M_PI * 2.0)
rotateAnimation.duration = duration
if let delegate: AnyObject = completionDelegate {
rotateAnimation.delegate = delegate //<- error "Cannot assign value of type 'AnyObject' to type 'CAAnimationDelegate?'"
}
self.layer.add(rotateAnimation, forKey: nil)
}
}
Trying to cast delegate as! CAAnimationDelegate will crash the application with error code Could not cast value of type 'test.ViewController' (0x1074a0a60) to 'CAAnimationDelegate' (0x10cede480) when trying to rotate the image.
Bending.com:
extension UIView {
func startRotating(duration: Double = 1) {
let kAnimationKey = "rotation"
if self.layer.animation(forKey: kAnimationKey) == nil {
let animate = CABasicAnimation(keyPath: "transform.rotation")
animate.duration = duration
animate.repeatCount = Float.infinity
animate.fromValue = 0.0
animate.toValue = Float(M_PI * 2.0)
self.layer.add(animate, forKey: kAnimationKey)
}
}
func stopRotating() {
let kAnimationKey = "rotation"
if self.layer.animation(forKey: kAnimationKey) != nil {
self.layer.removeAnimation(forKey: kAnimationKey)
}
}
}
When trying to call my image view to start rotating in my viewDidLoad method, nothing happens.
Errors explained:
You're passing the delegate in as AnyObject
func rotate360Degrees(duration: CFTimeInterval = 1.0, completionDelegate: AnyObject? = nil)
AnyObject doesn't conform to CAAnimationDelegate, so you can't use it as the delegate.
If you're certain you're passing in a controller that can be a proper delegate, then cast it to the right kind of delegate:
if let delegate = completionDelegate as? CAAnimationDelegate {
rotateAnimation.delegate = delegate
}
If you're doing this and it's crashing:
rotateAnimation.delegate = completionDelegate as! CAAnimationDelegate
Then you're not passing in a controller that conforms to CAAnimationDelegate
It might be simpler to not pass the delegate in at all and assign it outside of the extension.
I have some problems with my version of this loadingOverlay singleton.
What's supposed to happen, is it comes onto the screen, with a view and a label that has the text, "Loading, please wait." or something like that. then if loading is longer than 2 seconds (i've changed it to 10 for debugging) the text changes to a random cute phrase.
first of all the animation that should change the text doesn't seem to happen. instead, the text just instantly changes.
more importantly, If, for some reason, my asynchronous call block is executed multiple times, I only want the most recent call to it to run, and I want the previous instances of it to terminate before running.
I was reading about callbacks and promises, which look promising. Is that a swifty pattern to follow?
by the way, as I'm learning swift and iOS, I've been experimenting, and I tried [unowned self] and now i'm experimenting with [weak self], but I'm not really certain which is most appropriate here.
// from http://stackoverflow.com/questions/33064908/adding-removing-a-view-overlay-in-swift/33064946#33064946
import UIKit
class LoadingOverlay{
static let sharedInstance = LoadingOverlay()
//above swifty singleton syntax from http://krakendev.io/blog/the-right-way-to-write-a-singleton
var overlayView = UIView()
var spring: CASpringAnimation!
var springAway: CASpringAnimation!
var hidden = false
private init() {} //This line prevents others from using the default () initializer for this class
func setupSpringAnimation(startY: CGFloat, finishY: CGFloat) {
overlayView.layer.position.y = startY
spring = CASpringAnimation(keyPath: "position.y")
spring.damping = 10
spring.fromValue = startY
spring.toValue = finishY
spring.duration = 1.0
spring.fillMode = kCAFillModeBackwards
}
func showOverlay() {
print("show overlay")
overlayView.alpha = 1
hidden = false
if let appDelegate = UIApplication.sharedApplication().delegate as? AppDelegate,
let window = appDelegate.window {
setupSpringAnimation(-window.frame.height / 2, finishY: window.frame.height / 2)
let overlayViewFramesize = 0.65 * min(window.frame.height, window.frame.width)
overlayView.frame = CGRectMake(0, 0, overlayViewFramesize, overlayViewFramesize)
overlayView.center = window.center
overlayView.backgroundColor = UIColor.greenColor()
overlayView.clipsToBounds = true
overlayView.layer.cornerRadius = overlayViewFramesize / 8
let label = UILabel(frame: CGRectMake(0,0,overlayViewFramesize * 0.8 , overlayViewFramesize))
label.text = " \nLoading, please wait\n "
label.tag = 12
overlayView.addSubview(label)
label.lineBreakMode = NSLineBreakMode.ByWordWrapping
label.numberOfLines = 0 //as many as needed
label.sizeToFit()
label.textAlignment = NSTextAlignment.Center
label.center = CGPointMake(overlayViewFramesize / 2, overlayViewFramesize / 2)
overlayView.bringSubviewToFront(label)
window.addSubview(overlayView)
overlayView.layer.addAnimation(spring, forKey: nil)
RunAfterDelay(10.0) {
if self.hidden == true { return }
//strongSelf boilerplate code technique from https://www.raywenderlich.com/133102/swift-style-guide-april-2016-update?utm_source=raywenderlich.com+Weekly&utm_campaign=ea47726fdd-raywenderlich_com_Weekly4_26_2016&utm_medium=email&utm_term=0_83b6edc87f-ea47726fdd-415681129
UIView.animateWithDuration(2, delay: 0, options: [UIViewAnimationOptions.CurveEaseInOut, UIViewAnimationOptions.BeginFromCurrentState, UIViewAnimationOptions.TransitionCrossDissolve], animations: { [weak self] in
guard let strongSelf = self else { return }
(strongSelf.overlayView.viewWithTag(12) as! UILabel).text = randomPhrase()
(strongSelf.overlayView.viewWithTag(12) as! UILabel).sizeToFit()
print ((strongSelf.overlayView.viewWithTag(12) as! UILabel).bounds.width)
(strongSelf.overlayView.viewWithTag(12) as! UILabel).center = CGPointMake(overlayViewFramesize / 2, overlayViewFramesize / 2)
}, completion: { (finished: Bool)in
print ("animation to change label occured")})
}
}
}
func hideOverlayView() {
hidden = true
UIView.animateWithDuration(1.0, delay: 0.0, options: [UIViewAnimationOptions.BeginFromCurrentState], animations: { [unowned self] in
//I know this is clunky... what's the right way?
(self.overlayView.viewWithTag(12) as! UILabel).text = ""
self.overlayView.alpha = 0
}) { [unowned self] _ in
//I know this is clunky. what's the right way?
for view in self.overlayView.subviews {
view.removeFromSuperview()
}
self.overlayView.removeFromSuperview()
print("overlayView after removing:", self.overlayView.description)
}
//here i have to deinitialize stuff to prepare for the next use
}
deinit {
print("Loading Overlay deinit")
}
}
What I basically wanted, was to be able to delay a block of code, and possibly cancel it before it executes. I found the answer here:
GCD and Delayed Invoking