How to cancel one SKaction if I have multiple SKactions - xcode

I'm having difficulty removing 1 SKaction. I know that if I remove both SKactions with removeAllActions() but how do I go about only removing 1 will still having the other Action?

You need to use runAction withKey:
yourNode.runAction(someAction, withKey: "key")
// and then sometime later...
yourNode.removeActionForKey("key")
Obviously it makes sense to define the keys somewhere a bit less flakey than as hard-coded key every time you use them...

Related

Play back timed events in real time (millisecond accuracy, or almost)

I'm writing an OS X app that records real time events that come from a serial port and saves them to disk. So each millisecond I get 7 integers that I need to save and later "play back" by drawing them in a sort of live line chart. The saving stuff to disk is already done; playing them back is where I'm stuck.
I've done this sort of thing before with other technologies (Arduino, Corona, Processing) where I get a main game loop in which I can do whatever I want time-wise. What I need to know is how set up some sort of 'onEnterFrame' loop to be able to compare the elapsed time to the first event in my stack and fire it when needed. In this loop I would need to tell my views to update themselves and I know Apple guides state that UI updates must happen in the main thread.
I'm a little lost on how to conceptually achieve this kind of onEnterFrame loop in Cocoa as the paradigm is quite different to what I already know.
Of the many things I encountered, this gave me good timing accuracy in my initial tests (1/10th of a millisecond error):
func test() {
print("test")
}
// Run test() every 1 millisecond
let timer = NSTimer.scheduledTimerWithTimeInterval(0.001, target: self, selector: #selector(test), userInfo: nil, repeats: true)

How to update timer interval in swift?

I am working on a game where the rate of change of an image changes over time. At first the image change every two seconds and then it will speed up.
var change = 2.0
func setupGame(){
change = 2.0
changeImage()
}
func changeImage(){
timer = NSTimer.scheduledTimerWithTimeInterval(change, target: self, selector: #selector(SecondViewController.changeColors), userInfo: nil, repeats: true)
change -= 0.1
}
The problem is that it speeds up too much and it doesn't stop. Eventually the change value becomes negative.
Any way to make this work?
You are forgetting two things.
First, you are creating a new repeating timer every time the timer fires. You need to invalidate the old timer before creating the new timer. And since you are going to replace the timer anyway, you do not want this to be a repeating timer; you need to say false instead of true.
Second, if you don't want change to keep getting smaller, you need some sort of condition where you check how small change has become and, if you don't want to get any faster, don't decrement it.

Skipping SKActions

At the beginning of my scene I'm running a bunch of different actions as a sort of intro to the level. I would like to make it skippable by the user. Is there a way to handle this with SKAction? SKAction is my bet, but perhaps there's a better way to do this. Any ideas?
This assumes you want tap to stop the actions:
A very easy but possibly unstable approach would be to just crank the speed of the Node the action is running on to an insanely high number, just remember to reset it if you need the node.
An easy approach that is more stable would be to save your actions and reapply them with a duration of zero.
The best way to do this is to create a Dictionary for your SKActions like so:
var actionDict = [String,SKAction]()
Then just save all your actions with keys so that you can access it in the future:
actionDict["moveUp"] = SKAction.moveToY(100,duration:100)
Now that you have keys for it, when running your actions, just assign the same key:
node.runAction(actionDict["moveUp"],withKey:"moveUp")
Then when you need to end it, just remove the desired actions:
node.removeActionForKey("moveUp")
And reapply with a duration of 0:
//if we want to retain the old duration, use copy
let action = actionDict["moveUp"].copy()
action.duration = 0
node.runAction(action,withKey:"moveUp")
Note, this method only works where the action is doing a To event, if it is doing a By event, you need to figure out the final destination point, and make it a moveTo a with duration of 0

What is considered overloading the main thread?

I am displaying information from a data model on a user interface. My current approach to doing so is by means of delegation as follows:
#protocol DataModelDelegate <NSObject>
- (void)updateUIFromDataModel;
#end
I am implementing the delegate method in my controller class as follows, using GCD to push the UI updating to the main thread:
- (void)updateUIFromDataModel {
dispatch_async(dispatch_get_main_queue(), ^{
// Code to update various UI controllers
// ...
// ...
});
}
What I am concerned about is that in some situations, this method can be called very frequently (~1000 times per second, each updating multiple UI objects), which to me feels very much like I am 'spamming' the main thread with commands.
Is this too much to be sending to the main thread? If so does anyone have any ideas on what would be the best way of approaching this?
I have looked into dispatch_apply, but that appears to be more useful when coalescing data, which is not what I am after - I really just want to skip updates if they are too frequent so only a sane amount of updates are sent to the main thread!
I was considering taking a different approach and implementing a timer instead to constantly poll the data, say every 10 ms, however since the data updating tends to be sporadic I feel that it would be wasteful to do so.
Combining both approaches, another option I have considered would be to wait for an update message and respond by setting the timer to poll the data at a set interval, and then disabling the timer if the data appears to have stopped changing. But would this be over-complicating the issue, and would the sane approach be to simply have a constant timer running?
edit: Added an answer below showing the adaptations using a dispatch source
One option is to use a Dispatch Source with type DISPATCH_SOURCE_TYPE_DATA_OR which lets you post events repeatedly and have libdispatch combine them together for you. When you have something to post, you use dispatch_source_merge_data to let it know there's something new to do. Multiple calls to dispatch_source_merge_data will be coalesced together if the target queue (in your case, the main queue) is busy.
I have been experimenting with dispatch sources and got it working as expected now - Here is how I have adapted my class implementation in case it is of use to anyone who comes across this question:
#implementation AppController {
#private
dispatch_source_t _gcdUpdateUI;
}
- (void)awakeFromNib {
// Added the following code to set up the dispatch source event handler:
_gcdUpdateUI = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0,
dispatch_get_main_queue());
dispatch_source_set_event_handler(_gcdUpdateUI, ^{
// For each UI element I want to update, pull data from model object:
// For testing purposes - print out a notification:
printf("Data Received. Messages Passed: %ld\n",
dispatch_source_get_data(_gcdUpdateUI));
});
dispatch_resume(_gcdUpdateUI);
}
And now in the delegate method I have removed the call to dispatch_async, and replaced it with the following:
- (void)updateUIFromDataModel {
dispatch_source_merge_data(_gcdUpdateUI, 1);
}
This is working absolutely fine for me. Now Even during the most intense data updating the UI stays perfectly responsive.
Although the printf() output was a very crude way of checking if the coalescing is working, a quick scrolling back up the console output showed me that the majority of the messages print outs had a value 1 (easily 98% of them), however there were the intermittent jumps to around 10-20, reaching a peak value of just over 100 coalesced messages around a time when the model was sending the most update messages.
Thanks again for the help!
If the app beach-balls under heavy load, then you've blocked the main thread for too long and you need to implement a coalescing strategy for UI updates. If the app remains responsive to clicks, and doesn't beach-ball, then you're fine.

Get rid of invisible UltraGridRow instances

I've got a grid that show tens of thousands of rows in total, obviously the user is only looking at a small fraction of those rows at any point in time, generally somewhere around 10~20 rows.
I enabled LoadOnDemand, which ensures that UltraGridRow instances and a couple of other objects will only be created when they move into the visible area:
grid.DisplayLayout.LoadStyle = LoadStyle.LoadOnDemand;
This works great (according to my memory profiler), but when I scroll up and down the whole table, i.e. trigger creation of UltraGridRows for all rows, then all UltraGridRows will get created and stay in memory forever.
How can I (ideally automatically, but if not, manually) get rid off the UltraGridRow objects for rows that are out of view? (Or just get rid of all UltraGridRows - they'll be recreated automatically anyway)
One brute force way I figured out is this:
var tmp = grid.DataSource;
grid.DataSource = null;
grid.DataSource = tmp;
It causes some side effects though, so is there some other way to get rid of UltraGridRows?
Btw, I tried these two guys, without success (trying true and false for the bool params).
grid.Rows.Refresh(RefreshRow.ReloadData, false, true);
grid.Rows.DeallocateCells(true);
I'm trying to get memory consumption down, and UltraGridRows are currently the main consumer (UltraGridRows by themselves aren't huuuge, they consume under 200 bytes each, which in my case means a couple of megabytes, just so you know what we are talking about).
This is Infragistics 9.2.
Any ideas?
Well, there's always reflection...
var m = typeof(RowsCollection)
.GetMethod("InternalClearHelper", BindingFlags.NonPublic | BindingFlags.Instance);
m.Invoke(grid.Rows, null);
This actually works for me, but it will clear the selection, so you have to remember that before invoking and restore the selection afterwards.

Resources