Cocos2d, swift find when Spritebuilder timeline animation completed via callback - swift

I have a callback setup on timeline in Spritebuilder.
and I have the timeline animation triggered. My goal is to disable touch until animation completes.
func triggerTimelineAction(nodeName: CCNode){
self.userInteractionEnabled = false;
nodeName.animationManager.runAnimationsForSequenceNamed("nodeMainAction")
nodeName.animationManager.setCompletedAnimationCallbackBlock(b: ??? )){
self.userInteractionEnabled = true;
}
}
Question: how do I declare callback keyframe and hookup/receive it.
UPD: i was told to pass a function in setCompletedAnim... so i made my code look like:
func animationCompleted(){
self.userInteractionEnabled = true;
}
func triggerTimelineAction(nodeName: CCNode){
self.userInteractionEnabled = false;
nodeName.animationManager.runAnimationsForSequenceNamed("nodeMainAction")
nodeName.animationManager.setCompletedAnimationCallbackBlock(b: #selector(animationCompleted))
}
on which I received error "Cannot convert value of type ()->() to expected argument type (AnyObject)->Void
Apparently this is more related to Cocos2d syntaxis. will dig further in that direction

So after a session with codementor I figured out that I was using the setCompletedAnimationCallbackBlock function improperly.
In this code
activeNode.animationManager.setCompletedAnimationCallbackBlock(b: ((AnyObject!) -> Void)
I was trying to pass arguments as AnyObject
when in fact this is a block that this function returns.
So this code has worked for me
activeNode.animationManager.setCompletedAnimationCallbackBlock { (object: AnyObject!) -> Void in
print(object)
}
where object is just firs time declared name for element that gets returned. The print function showed that object that returns is a CCAnimationManager so it is equal to previous syntax when you had to runAction then assign action.delegate to self and then actually catch callback.
Hope this information helps somebody.

Related

Swift: Can't seem to store the resulting place of the placesClient.currentPlace as the class property to use elsewhere

I'm pretty new to this, but I've been managing to stumble my way through to getting the current location of my IOS device...etc The only problem is, I can't seem to get the GMSPlace to stay assigned to the property I declared at the top of my class, which I plan on using in another function.
It works fine when I run a print statement from within the scope of the callback, but when I seem to use the value stored in 'queryPlace', it returns a 'nil'. I'm guessing its a scope and lifetime issue, but I'm not sure if I understand it properly.
Here is the code that I'm having difficulty with understanding why it won't hold the value of 'place':
import UIKit
import CoreLocation
import GoogleMaps
import GooglePlaces
class GoogleMapSearchVC : UIViewController, CLLocationManagerDelegate {
var placeQuery: GMSPlace?
func loadCurrentPosition() {
print("Loading Positions and Coords")
// Invoke Callback method to get GMSPlacesLiklihood
placesClient.currentPlace(callback: { (placeLikelihoodList, error) -> Void in
if let error = error {
print("Pick Place error: \(error.localizedDescription)")
return
}
if let placeLikelihoodList = placeLikelihoodList {
let place = placeLikelihoodList.likelihoods.first?.place
if let place = place {
self.updateMap(newLocation: place)
// updateMap function moves camera to current location.
self.placeQuery = place
// If I print(self.placeQuery) here, it works fine, but later on placeQuery returns nil.
}
}
})
}
func doSomethingWithPlace() {
print(self.placeQuery?coordinate.latitude)
// Returns 'nil'
}
}
Thank you in advance for any help, very much appreciated.
No, there shouldn't be any "lifetime" issues. placeQuery is bound to the lifetime of your view controller instance.
Just a stupid guess (sorry if that was obvious): Are you sure that doSomethingWithPlace() accesses the variable after the callback has returned? Since you're setting it only in the asynchronous callback, the variable will not be set when loadCurrentPosition() returns.
If that's not the issue, here's a debugging tip to find out where this value is set back to nil: You can set a breakpoint at the line of the variable declaration and the debugger will break in the setter. If you're more into "print debugging", you can also add a didSet clause:
var placeQuery: GMSPlace? {
didSet {
print("setting placeQuery from \(oldValue) to \(placeQuery)")
}
}

Check for certain statements in Swift closure

I wrote a function which takes a closure as an argument like this:
func doSome(work: () -> Void = { print("sleeping...") } ) {
work()
}
Now I would like to investigate the work done.
Therefore I want to check if the given closure contains any print statements.
Somehow like this:
func doSome(work: () -> Void = { print("doing hard work...") } ) {
work()
if work.contains(print) {
print("we did some hard work there and printed something!")
}
}
How can I achieve that?
EDIT: What I am trying to achieve
An async function tries to connect to an http server - let's call it connect. It takes a closure as its parameter - called finally. As its name already indicates: the closure gets executed after the connecting attempt.
If the connecting attempt succeeds (http response code == 200), I need to call another function ONCE - let's call it so: once.
The connect function therefore looks like this:
func connect(finally: () -> Void = {}) {
httpRepsonse = asyncRequestToServer()
if httpResponse.statusCode == 200 {
once()
}
// and finally:
finally()
}
Other functions call connect and pass over their statements that they need for the connect function to execute finally.
And here comes the problem: there is one function that needs once executed every time, therefore it passes it over in the finally closure. If the connecting now succeeds, once gets called twice.
That's why I wanted to check the given closure already contains the once call, so I could avoid calling it twice.
Interrogating a closure for its contents is not easily done as far as I know.
You could do a workaround (depending on your needs and implementation of course) using one or more Boolean arguments which you would assign when calling the function, if relevant.
For example:
func doSome(work: () -> Void = { print("doing hard work...")}, containsPrint: Bool = false) {
// Call your work closure
work()
// Check conditions
if containsPrint {
print("We printed some stuff")
}
}
I am aware that this is a rather simple solution but it might provide the required functionality.
Use a global variable that you change whenever you print to the console, and check it inside you doSome(work:)
Short answer: You can't. As Alexander says, Swift does not support this. You would have to add some sort of housekeeping, as suggested in Carpsen90's answer.

Swift Closure Completion Handler

I have a function right now that is determining the hide behavior of a UILabel: func shouldHideLabel() -> Bool.
I need to retrieve data from a web request to determine whether or not to hide this label, so inside of shouldHideLabel() I'm making a call to a function func webRequestDataIsValid() -> Bool.
webRequestDataIsValid() is doing a web request with a closure, returning an object, and the object has a variable isValid on it, which is returning true or false.
My goal is to wait on this isValid flag to be returned to me, return from webRequestDataIsValid with true or false, and then use that return value to return from the original shouldHideLabel function.
I'm using a completion handler inside of shouldHideLabel to wait on the data from webRequestDataIsValid, but I'm not sure how to wait on returning inside of shouldHideLabel for my closure to finish. It seems like you can't return from a function while inside of a closure.
Any help is much appreciated. Thank you!
I would assume that your web request will contain a completionBlock,
try this:
func webRequestDataIsValid(completion: #escaping ((Bool) ->Void)) {
whateverYourRequestMethod.response { httpResponse in
let isValid = true // retrieve your boolean
completion(isValid)
)
}

Piping NSEventMask together in 'addLocalMonitorForEventsMatchingMask'

I have the following code for listening for mouse events outside the view (subclass of NSView). This works great as it is, but I cannot figure out how to pipe the NSEventMasks together. I basically want to have the same event listener fire on NSEventMask.LeftMouseDownMask, NSEventMask.RightMouseDownMask and NSEventMask.OtherMouseDownMask.
Does anyone know how to pipe them together or if it's even possible in Swift?
Working code
localMouseEventListener = NSEvent.addLocalMonitorForEventsMatchingMask(NSEventMask.LeftMouseDownMask) { (event: NSEvent) -> NSEvent? in
// Event handling...
return event
}
This answer might help you.
In short you can now use arrays, so this seems to make the compiler happy:
localMouseEventListener = NSEvent.addLocalMonitorForEventsMatchingMask([.LeftMouseDownMask, .RightMouseDownMask, .OtherMouseDownMask]) { (event: NSEvent) -> NSEvent? in
// Event handling...
return event
}

How to return a value to a function while in background process subfunction in swift

I first tried this solution to return a bool in the spot I want to return it. However, due to the parse.com function saveInBackgroundWithBlock() being a void return function, I got the error "Unexpected non-void return value in void function".
func saveObjectToParse(gameLocal: Game) -> Bool {
let game = PFObject(className:"Game")
game["sport"] = gameLocal.sport.rawValue
var saved = false
game.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if (success) {
print("Object has been saved.")
saved = true
return saved
} else {
print("parse error")
return saved
}
}
}
So, I tried moving the return statements out of the subfunction like this:
func saveObjectToParse(gameLocal: Game) -> Bool {
let game = PFObject(className:"Game")
game["sport"] = gameLocal.sport.rawValue
var saved = false
game.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if (success) {
print("Object has been saved.")
saved = true
} else {
print("parse error")
}
}
return saved
}
However, this returns saved before the saveInBackgroundWithBlock() block executes because it is a background process. Therefore, saved will never be true, even when it is intended to be. I have tried adding a boolean flag called done and tried waiting with a while(!done) loop, but this freezes the program on the loop and the background process never executes. How can I fix these problems?
I agree with restructuring not needing a bool returned, but if you really, really need this set up, you could save your object synchronously (so your code will wait) like so,
do {
try game.save()
} catch {
print(error)
}
Returning a value from a function but from another function doesn't make architectural sense. Nor is it possible.
You either will need to change your implementation and make both methods independent or think of using a semaphore.
http://www.g8production.com/post/76942348764/wait-for-blocks-execution-using-a-dispatch
What you are trying to do (create a helper function to wrap the Parse save function) makes perfect sense and can be easily accomplished.
You do not need to use semaphores and you certainly don't want to perform the operation synchronously. Instead, use a completion hander to let you know when the save has completed. For more information on completion handlers see this link
func saveObjectToParse(gameLocal: Game, completion: (gameSaved: Bool) -> Void) {
let game = PFObject(className:"Game")
game["sport"] = gameLocal.sport.rawValue
game.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
// Set the completion handler to be result of the Parse save operation
completion(gameSaved: success)
}
}
You may then call this function like so
saveObjectToParse(someGameObject) { (gameSaved: Bool) in
if gameSaved {
print("The game has been saved.")
} else {
print("Error while saving the game")
}
}
Using this technique, you could similarly propagate the entire callback of saveInBackgroundWithBlock through your function so you could inspect errors when they occur.
Edit: It looks like you may also be using your own custom class to represent the Game object. I would recommend looking into subclassing PFObject so you can easily and directly model your Parse classes. More details in the documentation