Is it possible to add a delay to an animation with Cocoa?
In my current code it shows a window and hides it with fade animation. What I am trying to do is add a delay before the fade animation.
#IBAction func doIt(_ sender: NSButton) {
openPanel()
NSAnimationContext.runAnimationGroup { (cont) in
cont.duration = 1.0
self.panel.animator().alphaValue = 0
}
//hide on completion
}
Wrap what you want to delay in a DispathQueue async call:
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { //delays 1 second
//code to delay
}
Related
Here is a section of my code where I am trying to delay a function called dropText that drops a name from the top of the screen. I tried using a delay function but it delays then drops them all at once. What am I missing, or is this method just plain wrong? Thanks in advance:
func delay(_ delay:Double, closure:#escaping ()->()) {
DispatchQueue.main.asyncAfter(
deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: closure)
}
//New group choose method
func groupChoose()
{
//loop through the players
for x in 0...players - 1{
//drop the name in from the top of the screen
delay(2.0) {
self.dropText(playing[x])
}
}
This issue is because you are delaying all of them at once! You should try to assign different delay time to each one:
for x in 1...players {
//drop the name in from the top of the screen
delay(2.0 * x) {
self.dropText(playing[x-1])
}
Refactored
Try to not call array elements by index:
for playing in playing.enumerated() {
// drop the name in from the top of the screen
let player = playing.offset + 1
delay(2.0 * player) {
self.dropText(playing.element)
}
Look at the loop. You are calling asyncAfter almost immediately one after another. So the text is dropped after the delay almost immediately one after another, too.
I recommend to use a Timer
func delay(_ delay: Double, numberOfIterations: Int, closure:#escaping (Int) -> Void) {
var counter = 0
Timer.scheduledTimer(withTimeInterval: delay, repeats: true) { timer in
DispatchQueue.main.async { closure(counter-1) }
counter += 1
if counter == numberOfIterations { timer.invalidate() }
}
}
and
func groupChoose()
{
delay(2.0, numberOfIterations: players) { counter in
self.dropText(playing[counter])
}
}
I want to change the alpha when the user pressed the button. I have used the below code and it works.
However, I am having to write this line of code for every button I connect to the assistance.
Is there any way that I can create a method/class/function to enable all buttons to have this function?
#IBAction func sundayButton(_ sender: UIButton) {
sender.alpha = 0.5
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {sender.alpha = 1.0
}
}
You can write extention on Button and call that on every button
extension UIButton {
func setAlpha() {
self.alpha = 0.5
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
self.alpha = 1.0
}
}
}
call it this way:
myButton.setAlpha()
I am trying to make a simple game with Xcode 11.2 which contains an animated loop background and a view which shows and changes various animations from several Lottie JSON files in project.
When I click the "Next" button to change the view animation, background view which is looping gets stuck for a second until the next animation inside the view is loaded an everything in the app freezes at the same time just like the background animation.
CPU usage varies from 30% to 63%.
I don't like to complicate the question, so I am just showing the way I used Lottie.
#IBAction func SubmitButtonAction(_ sender: UIButton) {
showNextQuestion()
}
func showNextQuestion()->(){
myTimer.invalidate()
startCountdown(fromTime: 15)
Manager.generateQuestion()
lblLevel.text = String(Manager.questionNumber) + "/" + String(DataModel.Questions.count)
nIndex = 0
let animation = AnimationView(name: Manager.currentImage)
animation.loopMode = .loop
animation.play()
animation.backgroundColor = UIColor.clear
animation.frame = self.AnimView.bounds
animation.backgroundBehavior = .pauseAndRestore
if AnimView.subviews.isEmpty{
self.AnimView.addSubview(animation)
}
else {
for one in imgImageView.subviews{
one.removeFromSuperview()
}
self.AnimView.addSubview(animation)
}
AnswerCollectionView.reloadData()
RandomCollectionView.reloadData()
}
I am interested in suggestions as to what the problem is related to - could it be related to a threading issue?
In response to Jaseel.Dev, I created a function that returned a LottieView with the following:
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
withAnimation(.spring()) {
LottieView(name: animation)
}
}
When I taped button UILabel appears and immediately disappears again. I need it to disappear after a few seconds. It's my first app and I can't solve this problem.
Thanks!
func done() {
if sauserImageView.isHidden == false && cupImageView.isHidden == false && spoonImageView.isHidden == false {
winningLabel.isHidden = false
}
}
You can perform a delayed action by using the DispatchQueue API, e.g.
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
self.label.isHidden = true
}
Or if you want to animate the hiding, use UIView.animate(withDuration:animations:) or UIView.animate(withDuration:delay:options:animations:completion:) e.g.:
UIView.animate(withDuration: 2) {
self.label.alpha = 0
}
Good luck!
Let me preface this question by saying I know it has been asked a few times, but the most upvoted post here was from back in 2014 and the top answer doesn't seem to work anymore.
Issue
In my game, I have all my 'moving' nodes as children of pauseNode. When the pauses button is pressed or when the player leaves the app, pauseScene() is called:
func pauseScene() {
self.node.speed = 0
self.physicsWorld.speed = 0
self.node.isPaused = true
}
This pauses all sprite nodes and emitter nodes. This keeps the sprite nodes paused if the app transitions from background to foreground state, but the particle emitters seem to resume animating.
Current solution
I solved this issue by triggering pauseScene() after a small delay when the app became active.
override func didMove(to view: SKView) {
NotificationCenter.default.addObserver(self, selector: #selector(GameScene.applicationDidBecomeActive(notification:)), name: NSNotification.Name.UIApplicationDidBecomeActive, object: app)
}
#objc func applicationDidBecomeActive(notification: NSNotification) {
NSDelay(0.01) {
pauseScene()
}
}
func NSDelay(_ delay:Double, closure:#escaping ()->()) {
let when = DispatchTime.now() + delay
DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}
Goal
While this solution pauses the particles, it is not perfect because they are visible moving for the split second it takes to pause them again (this is the shortest delay that actually re-pauses them).
So, after reading the post I linked above, I tried to override applicationDidBecomeActive() in an SKView subclass as was suggested, but I couldn't get the method to be called. I want to prevent these particles from being unpaused in the first place, if possible. Thanks
So, it's been a little over a day and I've found a better solution. A comment in this post suggested having a variable that recorded the status of the scene, and updating it along with the pause/play functions:
var sceneIsPaused = false
func pauseScene() {
self.node.speed = 0
self.physicsWorld.speed = 0
self.node.isPaused = true
sceneIsPaused = true
}
func playScene() {
self.node.speed = 1
self.physicsWorld.speed = 1
self.node.isPaused = false
sceneIsPaused = false
}
Then, override SKView's update method and check the state of that variable. If the scene should be paused, pause it. This will mean that if the scene is automatically unpaused it will re-pause the next frame. This is much faster and cleaner than setting a delay:
override func update(_ currentTime: TimeInterval) {
if (sceneIsPaused == true) {
self.node.speed = 0
self.physicsWorld.speed = 0
self.node.isPaused = true
}
}