Why is the completion never called?
I'm terribly sorry about this, a code dump... because I have no idea why every part of this works, except for the calling of the completion.
The SKAction that's not calling its completion runs, all except for the completion. It is this:
curtain.run(fadeMoveWipeAndReveal, completion: {onDone(), print("I'm done!!!!")})
Within the following class:
import SpriteKit
class WipeCurtain: SKSpriteNode {
var wipeCurtainBase: SKSpriteNode?
var returnable = SKNode()
var moveRight = SKAction()
var node = SKNode()
init( color: SKColor,
size: CGSize,
time: TimeInterval,
reveal: @escaping () -> (),
onDone: @escaping () -> ())
{
super.init(texture: nil, color: color, size: size)
wipeCurtainBase = SKSpriteNode(color: color, size: size)
let show = SKAction.run(reveal)
let fadeIn = createFadeIn(duration: time)
let moveRight = createMoveRight(duration: time)
let wipeRight = createWipeRight(duration: time)
let fadeAndMove = SKAction.group( [ fadeIn, moveRight ] )
let wipeAndReveal = SKAction.group( [ show, wipeRight ] )
let fadeMoveWipeAndReveal = SKAction.sequence( [ fadeAndMove, wipeAndReveal ] )
if let curtain = self.wipeCurtainBase {
curtain.anchorPoint = CGPoint(x: 1, y: 0)
curtain.position = CGPoint(x: frame.size.width, y: 0)
curtain.zPosition = -1
curtain.name = "wipe"
curtain.run(fadeMoveWipeAndReveal, completion: {
onDone()
print("I'm done!!!!")
})
}
}
func createFadeIn(duration: TimeInterval) -> SKAction {
let fadeIn = SKEase
.fade(
easeFunction: .curveTypeLinear,
easeType: .easeTypeIn,
time: duration,
fromValue: 0,
toValue: 1
)
return fadeIn
}
func createMoveRight(duration: TimeInterval) -> SKAction {
let moveRight = SKEase
.move(
easeFunction: .curveTypeExpo,
easeType: .easeTypeOut,
duration: duration,
origin: CGPoint(
x: 0,
y: 0),
destin: CGPoint(
x: frame.size.width,
y: 0)
)
return moveRight
}
func createWipeRight(duration: TimeInterval) -> SKAction {
let wipeRight = SKEase
.createFloatTween(
start: 1,
ender: 0,
timer: duration,
easer: SKEase
.getEaseFunction(
.curveTypeExpo,
easeType: .easeTypeOut
),
setterBlock: {(node, i) in
node.xScale = i}
)
return wipeRight
}
func wipeWith() -> SKNode {
if let curtain = wipeCurtainBase?.copy() as! SKSpriteNode? {
returnable = curtain
}
return returnable
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Here's the gameScene that makes this work, a very lightly modded version of the Xcode SpriteKit template:
import SpriteKit
import GameplayKit
class GameScene: SKScene {
private var swipe: WipeCurtain?
override func didMove(to view: SKView) {
swipe = WipeCurtain(color: .brown, size: frame.size, time: 1,
reveal: self.showOrNot,
onDone: self.previewer
)
}
func showOrNot(){
print(" Deciding to show or not.......")
}
func previewer(){
print("now running the previewer")
}
func touchDown(atPoint pos : CGPoint) {
addChild(swipe!.wipeWith())
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchDown(atPoint: t.location(in: self)) }
}
}
I want to focus your attention to these few lines:
The offending code is:
curtain.run(fadeMoveWipeAndReveal, completion: {
onDone()
print("I'm done!!!!")
})
where you can substitute fadeMoveWipeAndReveal
with one of the other action like simply show
:
let show = SKAction.run(reveal)
and you will see the completion is never called. Why? Because it run the first time during the WipeCurtain initialization but the action is removed before it completes, so the next time you re-call this one to run through touchesBegan
, completion will never called.
You can test it by putting a breakpoint to the curtain.run
code and launch the project:
as you can see the breakpoint stop the project immediatly during initialization, in this phase, action is removed before it completes.
About your workaround,that's correct, it works because you remove completion
and use the simply SKAction.sequence
that is executed correctly every time you call it.
Details:
The copy
method works for me, I suspect some people have problems with it because around internet there are more versions about the SKEase and maybe some of that could have a problem, but this version works well as demonstrated by these two screenshots below: