-(CCNode *)createFieldNode:(NSMutableArray *)fieldArray{
CGSize winSize = [CCDirector sharedDirector].viewSize;
CCNode* stackNode= [CCNode node];
for (int i; i <=fieldArray.count; i++){
//itemP is previous item in array and itemC is current item in area based on index i
BPItem*itemP;
BPItem*itemC;
if(i!=0){
itemP=[fieldArray objectAtIndex:i-1];
itemC=[fieldArray objectAtIndex:i];
float stackWidth=arc4random()%200+50;
float stackHeight=itemP.position.y+itemP.contentSize.height;
itemC.position=ccp(stackWidth,stackHeight);
}
else{
itemC=[fieldArray objectAtIndex:i];
float stackWidth=arc4random()%200+50;
itemC.position=ccp(stackWidth,0);
}
//having trouble adding multiple nodes to stackNode
[stackNode addChild:itemC];
}
return stackNode;
}
I want to add CCNodes from fieldArray on to a parent CCNode "stackNode". When I use breakpoints I am able to add the CCNode at index 0 and CCNode at index 1. However the program crash at i=2. The error I receive is:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'child already added to another node. It can't be added again'
Before the crash "stackNode" has two children. I'm not adding the CCNodes one by one because I have hundreds of different arrays with many a fieldArray.count around 20. Please help I can explain more if I have been unclear.
Change the for loop start as follows :
//itemP is previous item in array and itemC is current item in area based on index i
BPItem*itemP;
BPItem*itemC; // moved out of the for loop
for (int i; i <fieldArray.count; i++){ // <- changed the end condition to avoid crash
... rest of loop
Also, in the code that creates fieldArray, make certain you have logic to ensure that there are no duplicates, otherwise you will have the same issue (but for an altogether different reason).
Related
In GMS2 I have a spawner item with the following code:
In the create event:
timer = 0;
In the step event:
if(distance_to_object(obj_coffe_bean) > 2)
if(timer == 200) {
instance_create_layer(x, y, obj_coffe_bean, obj_coffe_bean);
timer = 0;
}
else timer++;
This works perfectly fine, coffee beans are spawned when it doesn't detect any coffee bean nearby.
The problem is that the same code doesn't work when I duplicate this object and create a spawner for another item.
The most obvious problem here would be that you are using the object index as the layer index in instance_create_layer - your code only works by chance (of there being a layer with a matching ID).
Ok, I needed to use instance_create_depth instead of instance_create_layer.
Starting point (boundary conditions):
There is a set of recurring events each week (weekly events section
row 9:11).
My colleagues can set a starting date A6 and how many weeks they'd like to add A5.
Start situation:
Goal:
Running the script should add A5 number of weeks starting from the date A6 to the "upcoming events" section (row 22 and following) with the correct date.
This is how it would look like after the script ran successfully:
What works so far:
The script is able to add the recurring events for one week in the right order to the upcoming events section. It works as well if the starting date is in the middle of the week. (Not shown here as it is probably not relevant.)
My code:
function recurringWeeks() {
var ss = SpreadsheetApp.getActiveSheet(); // selects the active spreadsheet
var repeatingWeeks = ss.getRange(5,1).getValue(); // gets how many weeks it should repeat
var regWeek = ss.getRange(9, 2, 3, 7).getValues(); // gets the regular week data
var regWeekRepeated = ss.getRange(9, 2, repeatingWeeks*3, 7); // create an array to store the events for all weeks
// fill regWeekRepeated with regWeek
for (var j = 0; j < repeatingWeeks; j++){
for (var i = 0; i < 3; i++){
regWeekRepeated[i+j*3] = regWeek[i];
}
}
// Repeat week for "A5" times and add to start/end date a week
for (var j = 0; j < repeatingWeeks; j++){
for (var i = 0; i < 3; i++){
regWeekRepeated[i+j*3][0] = new Date(regWeek[i][0].getTime() + j*7*3600000*24); // <-This line leads to an error message.
}
}
ss.getRange(ss.getLastRow()+1,2,repeatingWeeks*3,7).setValues(regWeekRepeated); // copies weekly events after the last row
}
Edit of [i+j*6] to [i+j*3] in Repeat week for "A5" times and add to start/end date a week
Approach:
As I have solved how to add one week of recurring events with the correct date and right order, I use this as my "point of attack". I'm pretty sure that a for-loop does the job and this is currently my preferred tool.
create an array (regWeek) filled with the recurring event for one
week with the right order and dates. DONE
create an array (regWeekRepeated) and fill it with A5 number of
regular weeks (regWeek) starting from the date A6. ERROR 1:
Object does not allow properties to be added or changed.
make changes to the filled array regWeekRepeated. ERROR 2: TypeError: Cannot set property "0.0" of undefined to "(class)#3d8e4650"
Copy the values into the "upcoming events" section. DONE
Best hit I've found in the search results:
creating 2 dimension arrays
However, this uses .push and as far as I understand this means an element (can be a row) is placed at the end of an array. I've tried to use push as well but have not been successful yet.
Questions:
ERROR 1: Why is it not possible to assign the value of an element from array regWeek to array regWeekRepeated? Solved
ERROR 2: Is this property issue related to ERROR 1 or something different? I've tried to solve both errors individually. Solved
Which approach makes more sense (logically or performance wise) in this context: push individual rows at the end of an existing array or use the whole week as array building blocks?
A demo version of the spreadsheet
Update V01:
Changes: regWeekRepeated is now an array.
I've changed the for loop due to the feedback I've received.
// fill regWeekRepeated with regWeek
var regWeekRepeated = [];
for (var j = 0; j < repeatingWeeks; j++){
for (var i = 0; i < 3; i++){
regWeekRepeated.push(regWeek[i]);
}
}
Logger.log(regWeekRepeated)
Update V02:
// Repeat week for "A5" times and add to start/end date a week
for (var j = 0; j < repeatingWeeks; j++){
for (var i = 0; i < 3; i++){
regWeekRepeated[i+j*3][0] = new Date(regWeek[i][0].getTime() + j*7*3600000*24); //adds a week to the dates for each cycle
//Logger.log(regWeekRepeated[i]); // log is as expected and desired
}
Logger.log(regWeekRepeated); // second part of log not as expected.
}
//Logger.log(regWeekRepeated);
ss.getRange(ss.getLastRow()+1,2,repeatingWeeks*3,7).setValues(regWeekRepeated); // copies weekly events after the last row
Here the log output placed in the "outer" for loop.
1 represents the first cycle, 2 the second cycle
It looks like the second for loop overwrites the elements 0 to 2.
And here the output in google sheets
Update V03:
This makes sure that the changes don't affect the copy.
// fill regWeekRepeated with regWeek
for (var j = 0; j < repeatingWeeks; j++){
for (var i = 0; i < 3; i++){
regWeekRepeated[i+j*3] = regWeek[i].slice(); // shallow copy of an array
}
}
regWeekRepeated is not a array. getRange() doesn't return a array.
Try changing from
var regWeekRepeated = ss.getRange(9, 2, repeatingWeeks*3, 7); // create an array to store the events for all weeks
To
var regWeekRepeated = ss.getRange(9, 2, repeatingWeeks*3, 7).getValues(); // create an array to store the events for all weeks
Creating a array without touching the spreadsheet will increase performance.
var regWeekRepeated =[];
Making scripts for spreadsheets could be tricked because the spreadsheet and JavaScript jargons/lexics use the same terms in different ways. Perhaps this is what is happening here.
ERROR 1: Why is it not possible to assign the value of an element from array regWeek to array regWeekRepeated?
regWeekRepeated is a Range object not a JavaScript Array
ERROR 2: Is this property issue related to ERROR 1 or something different? I've tried to solve both errors individually.
Yes it's related. See the previous answer.
Which approach makes more sense (logically or performance wise) in this context: push individual rows at the end of an existing array or use the whole week as array building blocks?
We could say that calling Google Apps Scripts classes and methods are "expensive" so we should try to minimize the number calls to these kind of elements. One way to do this is by passing the range values to a JavaScript Array , then make all the changes directly to it and we finish pass the resulting values to the corresponding range.
To pass the values of a range to a JavaScript 2D array, use range.getValues() and to pass the JavaScript 2D array values to a range use range.setValues().
I have an NSTableView which gets populated without bindings.
In the datasource method tableView:objectValueForTableColumn:row:, I re-sort my data and tell the table view to reload after committing the edit to the model, so that the correct sort order is restored after editing.
If the user ends editing by clicking a different row, however, and the sort order has changed because of the edit that has just been ended, it might happen that the row the user intended to select has just moved after he clicked it. So instead of that row, a different row that is now in that spot gets selected.
I tried various combinations of the NSTableViewDelegate methods, but could not find a solution that adjusts the selection in a way that when a re-sort after editing has ended has occurred, the row that has just moved away gets selected. How can I achieve this?
I would use this:
I recommend to sort your data in NSTableView delegate setObjectValue: instead of objectValueForTableColumn:
And change the selection in NSTableView delegate selectionIndexesForProposedSelection:
This solution manages a single selection, If you want it manages single and multiple selection, I would change my answer.
If the user double-click, it doesn't work since it changes the selection to the second click.
You need these variables :
int originalRowIndex;
NSUInteger newRowIndex;
I initialize this variable: originalRowIndex = -1;
- (void)tableView:(NSTableView *)aTable setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)aColumn row:(int)rowIndex {
id objectAtRow = [myMutableArray objectAtIndex:rowIndex];
NSString *columnKey = [aColumn identifier];
[objectAtRow setValue:anObject forKey:columnKey];
originalRowIndex = rowIndex; // get the original index
[myMutableArray sortUsingDescriptors:[aTable sortDescriptors]]; // re-sort the mutable array
newRowIndex = [myMutableArray indexOfObjectIdenticalTo:objectAtRow]; // get the new index
if (newRowIndex == originalRowIndex) originalRowIndex = -1; // same position
}
// not called on empty selection
- (NSIndexSet *)tableView:(NSTableView *)tableView selectionIndexesForProposedSelection:(NSIndexSet *)proposedSelectionIndexes {
int oIndex = originalRowIndex;
originalRowIndex = -1;
if (oIndex > -1 && [proposedSelectionIndexes count] > 1) { // if oIndex = -1 or multi selection , do nothing
int i = [proposedSelectionIndexes firstIndex];
if (oIndex < newRowIndex) {
if (i > oIndex && i <= newRowIndex) return [NSIndexSet indexSetWithIndex:(i - 1)];//shift the row index
} else {
if (i < oIndex && i >= newRowIndex) return [NSIndexSet indexSetWithIndex:(i + 1)];//shift the row index
} //else doesn't change the selection, this index is out of range (originalIndex...newIndex)
}
return proposedSelectionIndexes;// doesn't change the selection
}
I've always done this the hard way: before doing something that needs the selection to be restored, I remember the current selection -- not by row index, but by something I can find in my data. Often I have an array of dictionaries, so I just remember the dictionary pointer.
After doing whatever I need to do on the tableview, I just go through the data again, looking for my object to find the new index...
I am trying to make a program that will have an NSTableView that I can add and remove values from, and for each value it will store a few different variables that will be displayed in text boxes when a user selects an item from the table. I've already wrote the code to add and remove values, but can't seem to find a way to get the rest of the functionality to work. How can I do this?
I suggest that you you represent each element in your tableview datasource (your array of objects) as a NSDictionary. This enables you to keep several variables for each tableview item, which can be displayed in textboxes when an item is clicked. Apple has a very good example that illustrates what I think you are trying to do. Take a look at the NSTableViewBinding example. In the example, when the user double-clicks an item in the tableview an inspect method is called. You can use this method to display your variables from the dictionary in textboxes:
- (void)inspect:(NSArray *)selectedObjects
{
// this is an example of inspecting each selected object in the selection
int index;
int numItems = [selectedObjects count];
for (index = 0; index < numItems; index++)
{
NSDictionary *objectDict = [selectedObjects objectAtIndex:index];
if (objectDict != nil)
{
NSLog(#"inspect item: {%# %#, %#}",
[objectDict valueForKey:#"firstname"],
[objectDict valueForKey:#"lastname"],
[objectDict valueForKey:#"phone"]);
[myTextBox1 setStringValue:[objectDict valueForKey:#"firstname"]];
[myTextBox2 setStringValue:[objectDict valueForKey:#"lastname"]];
}
}
}
Mac OS X 10.5 compatibility, Lua 5.0 compatibility required (hence cannot use current batch of LuaObjc bridges.)
My lua script produces an indexed table containing tens of thousands of strings.
Basic problem: how to concat those strings with a newline separator, to one string, quickly?
Fly in ointment: even using garbage-collection friendly concat code (provided at stackoverflow) the results take far too long for this purpose. (10 seconds vs 1 minute for a brute force solution.)
Proposed solution: offload the job to Cocoa, where it can be done in a fraction of a second, using NSArray's -componentsJoinedByString method.
New fly in ointment: how to get table data from Lua to Cocoa?
The script calls a registered C function, passing it the table. The C function tries to grab the table on the stack:
// Get an NSArray of strings from the first argument on the stack (a table).
NSArray *strings = nsArrayFromIndexedTable(luaState, index_1Based);
...
// Given a simple table consisting of numbers or strings, returns an NSArray.
// Nested subtables are not followed.
NSArray * nsArrayFromIndexedTable(lua_State *L, int argIdx)
{
// (Allegedly) stops GC.
lua_setgcthreshold(L, INT_MAX);
// Arg must be a table.
luaL_checktype(L, argIdx, LUA_TTABLE);
// Get num elements in table, build an array with that many.
int count = luaL_getn(L, 1);
NSMutableArray *array = [NSMutableArray arrayWithCapacity: count];
int i;
for (i = 1; i <= count; i++) {
lua_rawgeti(L, argIdx, i);
int valueType = lua_type(L, -1);
id value = 0x00;
if (valueType is_eq LUA_TNUMBER) {
value = [NSNumber numberWithDouble:lua_tonumber(L, -1)];
} else if (valueType is_eq LUA_TSTRING) {
value = [NSString stringWithUTF8String:lua_tostring(L, -1)];
}
if (value) {
[array addObject:value];
}
}
// Resume GC
lua_setgcthreshold(L, 0); // INTERMITTENT EXC_BAD_ACCESS CRASH HERE!!!!
return array;
}
Problem: calling this function with a (very large) Lua table of strings (intermittently) results in a EXC_BAD_ACCESS.
Debugger results are sporadic; sometimes not providing anything useful, but I've been able to glean that:
If those Lua GC lines included, the crash happens at lua_setgcthreshold, near the end of the function.
But... if those Lua GC lines are commented out, the crash happens at [array addObject:value]
(NSZombieEnabled is on, but is not providing useful info.)
Any help is appreciated.
This:
int count = luaL_getn(L, 1);
Should be:
int count = luaL_getn(L, argIdx);
So you may be getting an incorrect row count and scanning off the end of the table.
Maybe you grow your C stack too much. I am not familiar with Cocoa, but I guess that the Lua values need not be accessible all the time - the string should be copied into NSString. If it is so, try including a lua_pop(L, 1) at the end of the loop, to clean up the C stack and keep it from growing.