I'm trying to do something I thought was going to be very simple but I can't figure it out. I'm filling an array with objects like this in TableViewController:
if (indexPath.row == 0) {
detailController.textArray = [[NSMutableArray alloc] initWithObjects:#"Text1", #"Text2", #"Text3", #"Text4", #"Text5", nil];
}
Then in the DetailController I added this action to make the label display the next object of the textArray:
- (IBAction)nexTextButtonPressed:(id)sender {
int i = 0;
if (i<[textArray count])
i++;
textLabel.text = [textArray objectAtIndex:i];
}
I connected the button in ib to the action with option Touch Down (tried some of the others too). Something isn't working, the button jumps to the second object of the array but then it doesn't work any more. What could be causing this and how to fix it?
int i = 0; is initialized inside the method, it will always be reinitialized to zero on every call which will not let you move forward from second object
Well every time you hit the button i is getting reset to 0 so when it hits the if statement yes it goes into the statement as the condition is correct, but then you are incrementing i by 1 so i becomes 1 then you retrieve the array index 1 every time. You aren't actually doing anything else. No loop to print through the array or anything.
Related
GMSprite *bulletMove;
int bulletCount = [bullets count];
for(int i = 0; i < bulletCount; i++)
{
if(bulletMove.position.x > 500)
{
[self removeChild:[bullets objectAtIndex:i] cleanup:YES];
}
}
How do i remove the child from the array and also the object in the array so that bulletCount goes down an integer and adjusts the array to the removed object
Use below code:
[bullets removeObjectAtIndex:i];
Get bullet object from bulet array and use below code.
[bullet removeFromParentAndCleanup:YES];
Use
[bullets removeObjectAtIndex:i];
How do i remove the child from the array and also the object in the
array so that bulletCount goes down an integer and adjusts the array
to the removed object
Using the above method will remove the object from index i and all following objects will shift one-up.
GMSprite *bulletMove;
for(int i = 0; i < [bullets count]; )
{
if(bulletMove.position.x > 500)
{
[bullets removeObjectAtIndex:i];
} else {
i++;
}
}
It is not exactly good style manipulating the index varialbe of a for loop within its body. You may want to re-structure this suggestions with antother type of loop (do-while or so). The basic idea is, however, that [bullest count] will always provide you with the current amount of entries in the array. And the index must only be increased if you do not remove the current object. If you remove it an your index is at 10 (example) then the next one to be checkt is at 10 again. If you remove that too then the next one to be checked against the 500 is agein at 10. So either remove it or increase the index. And as exit criteria of the loop check the index against the current amount of objects in the array.
Edit: Second part of your question: If you do your memory management right, regardless wether you ARC or not, removeObjectAtIndex should properly remove the object itself. (Unless its retain count was higher than 1 or another strong reference still exists. But even then it reduces the retain count by 1 and does exactly the right thing.)
Annoying newbie problem here. This variable isPlayerTouchingAnotherPlayer is being set to true as soon as I touch the piece. I'm almost positive I know why but I can't find a way to display this in log to confirm. I could probably do it by setting different flag numbers for each of the objects but I was hoping there is another way.
The problem is that piece is an object that is also located in p1Array so as soon as I touch it it hits itself and isPlayerTouchingAnotherPlayer is evaluated to true.
Is there a way I could print out the view or image name of the objects touching somehow to confirm this? And furthermore, is there a way to somehow avoid this annoying conflict.
for (int i = 0; i <[p1Array count]; i++) {
UIImageView *tempP1;
tempP1 =[p1Array objectAtIndex:i];
if (CGRectIntersectsRect(piece.frame, tempP1.frame)) {
NSLog(#"selected piece: %#, touched piece: %# ", piece, tempP1);
isPlayerTouchingAnotherPlayer = TRUE;
}
}
why not use fast enumeration and skip the image view you are not interested in checking.
for (UIImageView *imageView in p1Array) {
if (imageView == piece)
continue;
if (CGRectIntersectsRect(imageView.frame, piece.frame)) {
// do whatever
}
}
it seems like you are already printing out the names of the objects touching in the code sample you have provided. if you want to print out specific properties of the objects you can do that to.
as soon as i touch it it hits itself and isPlayerTouchingAnotherPlayer
is evaluated to true.
Then you should get a log message that shows the same object for the selected piece and the touched piece. If that's what's happening, then just add a condition to your if to prevent it:
if (piece != tempP1 && CGRectIntersectsRect(piece.frame, tempP1.frame)) {
Simply
NSLog(#"%# %#", piece, tempP1);
or
NSLog(#"%ld %ld", (NSInteger) piece, (NSInteger) tempP1);
The first will show you the description of the object, the second the address in memory, if it's same address, it's the same object.
You can simply check if it's the same object (pointer) with a simple equal check to exclude the same object :
if (piece != tempP1) {
if (CGRectIntersectsRect(piece.frame, tempP1.frame)) {
...
}
Furthermore, you would like to write this:
for ( int i = 0;
( ( i < [p1Array count] )
&& ( ! isPlayerTouchingAnotherPlayer )
);
i++
) {
...
}
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
Let's say I have 2 UITableViews next to eachother on an ipad in landscape-mode.
Now I want to move multiple items from one tableView to the other. They are allowed to be inserted on the bottom of the other tableView. Both have multiSelection activated.
Now the movement itself is no problem with normal cells. But in my program each cell has an object which contains the consolidationState of the cell. There are 4 states a cell can have: Basic, Holding, Parent, Child.
Basic = an ordinary cell.
Holding = a cell which contains multiple childs but which wont be shown in this state.
Parent = a cell which contains multiple childs and are shown directly below this cell.
Child = a cell created by the Parent cell.
The object in each cell also has some array which contains its children. The object also holds a quantityValue, which is displayed on the cell itself.
Now the movement gets tricky. Holding and Parent cells can't move at all. Basic cells can move freely. Child cells can move freely but based on how many Child cells are left in the Parent. The parent will change or be deleted all together.
If a Parent cell has more then 1 Child cell left it will stay a Parent cell.
Else the Parent has no or 1 Child cell left and is useless. It will then be deleted.
The items that are moved will always be of the same state. They will all be Basic cells.
This is how I programmed the movement:
*First I determine which of the tableViews is the sender and which is the receiver.
*Second I ask all indexPathsForSelectedRows and sort them from highest row to lowest.
*Then I build the data to be transferred. This I do by looping through the selectedRows and ask their object from the sender's listOfItems.
*When I saved all the data I need I delete all the items from the sender TableView. This is why I sorted the selectedRows so I can start at the highest indexPath.row and delete without screwing up the other indexPaths.
*When I loop through the selectedRows I check whether I found a cell with state Basic or Child.
*If its a Basic cell I do nothing and just delete the cell. (this works fine with all Basic Cells)
*If its a Child cell I go and check it's Parent cell immidiately. Since all Child cells are directly below the Parent cell and no other the the Parent's Childs are below that Parent I can safely get the path of the selected Childcell and move upwards and find it's Parent cell. When this Parent cell is found (this will always happen, no exceptions) it has to change accordingly.
*The Parent cell will either be deleted or the object inside will have its quantity and children reduced.
*After the Parent cell has changed accordingly the Child cell is deleted similarly like the Basic cells
*After the deletion of the cells the receiver tableView will build new indexPaths so the movedObjects will have a place to go.
*I then insert the objects into the listOfItems of the receiver TableView.
The code works in the following ways:
Only Basic cells are moved.
Basic cells and just 1 child for each parent is moved.
A single Basic/Child cell is moved.
The code doesn't work when:
I select more then 1 or all childs of some parent cell.
The problem happens somewhere into updating the parent cells. I'm staring blindly at the code now so maybe a fresh look will help fix things. Any help will be appreciated.
Here is the method that should do the movement:
-(void)moveSelectedItems
{
UITableView *senderTableView = //retrieves the table with the data here.
UITableView *receiverTableView = //retrieves the table which gets the data here.
NSArray *selectedRows = senderTableView.indexPathsForSelectedRows;
//sort selected rows from lowest indexPath.row to highest
selectedRows = [selectedRows sortedArrayUsingSelector:#selector(compare:)];
//build up target rows (all objects to be moved)
NSMutableArray *targetRows = [[NSMutableArray alloc] init];
for (int i = 0; i<selectedRows.count; i++)
{
NSIndexPath *path = [selectedRows objectAtIndex:i];
[targetRows addObject:[senderTableView.listOfItems objectAtIndex:path.row]];
}
//delete rows at active
for (int i = selectedRows.count-1; i >= 0; i--)
{
NSIndexPath *path = [selectedRows objectAtIndex:i];
//check what item you are deleting. act upon the status. Parent- and HoldingCells cant be selected so only check for basic and childs
MyCellObject *item = [senderTableView.listOfItems objectAtIndex:path.row];
if (item.consolidatedState == ConsolidationTypeChild)
{
for (int j = path.row; j >= 0; j--)
{
MyCellObject *consolidatedItem = [senderTableView.listOfItems objectAtIndex:j];
if (consolidatedItem.consolidatedState == ConsolidationTypeParent)
{
//copy the consolidated item but with 1 less quantity
MyCellObject *newItem = [consolidatedItem copyWithOneLessQuantity]; //creates a copy of the object with 1 less quantity.
if (newItem.quantity > 1)
{
newItem.consolidatedState = ConsolidationTypeParent;
[senderTableView.listOfItems replaceObjectAtIndex:j withObject:newItem];
}
else if (newItem.quantity == 1)
{
newItem.consolidatedState = ConsolidationTypeBasic;
[senderTableView.listOfItems removeObjectAtIndex:j];
MyCellObject *child = [senderTableView.listOfItems objectAtIndex:j+1];
child.consolidatedState = ConsolidationTypeBasic;
[senderTableView.listOfItems replaceObjectAtIndex:j+1 withObject:child];
}
else
{
[senderTableView.listOfItems removeObject:consolidatedItem];
}
[senderTableView reloadData];
}
}
}
[senderTableView.listOfItems removeObjectAtIndex:path.row];
}
[senderTableView deleteRowsAtIndexPaths:selectedRows withRowAnimation:UITableViewRowAnimationTop];
//make new indexpaths for row animation
NSMutableArray *newRows = [[NSMutableArray alloc] init];
for (int i = 0; i < targetRows.count; i++)
{
NSIndexPath *newPath = [NSIndexPath indexPathForRow:i+receiverTableView.listOfItems.count inSection:0];
[newRows addObject:newPath];
DLog(#"%i", i);
//scroll to newest items
[receiverTableView setContentOffset:CGPointMake(0, fmaxf(receiverTableView.contentSize.height - recieverTableView.frame.size.height, 0.0)) animated:YES];
}
//add rows at target
for (int i = 0; i < targetRows.count; i++)
{
MyCellObject *insertedItem = [targetRows objectAtIndex:i];
//all moved items will be brought into the standard (basic) consolidationType
insertedItem.consolidatedState = ConsolidationTypeBasic;
[receiverTableView.ListOfItems insertObject:insertedItem atIndex:receiverTableView.ListOfItems.count];
}
[receiverTableView insertRowsAtIndexPaths:newRows withRowAnimation:UITableViewRowAnimationNone];
}
If anyone has some fresh ideas of why the movement is bugging out let me know. If you feel like you need some extra information I'll be happy to add it.
Again the problem is in the movement of ChildCells and updating the ParentCells properly.
I could use some fresh looks and outsider ideas on this.
Thanks in advance.
*updated based on comments
With this code there were 2 Bugs left unsolved:
Bug1: selecting them all, then moving them leaves a Child cell
Bug2: when Parent-Child-Child are the last 3 items in the list and then you try to select them all and move them it crashes.
Again single item movement worked perfect at this time.
Just when you thought it was over a single bug remains:
When you move all but a single ChildCell from a ParentCell the ChildCell wont get changed back into the BasicCell. This is an important aspect that needs to be added since it might screw up a second movement from the table.
When you have a situation as follows:
P-C-C-C-C-P-C-C
And you select(marked with x) like this:
P-C-C-C-C-P-C-Cx
It acts like this after the movement:
P-C-C-C-C-C
While expecting:
P-C-C-C-C-B
Which will screw up the movement the next time you make it.
The Parent here has a quantity of 4. But there are 5 childcells behind it.
When you are selecting the cells like this now:
P-Cx-C-Cx-C-Cx
while expected:
P-Cx-C-Cx-C-Bx
The parent shouldn't be deleted since only 2 of its 4 children are selected. But thanks to the previous movement and the code as it is now he will think 3 of its children are removed and will return this:
C-C
While expected:
P-C-C
Replacing the item in the datasource proved to be a horrendous task. It just didn't seem to stick.
This last bug was solved now as well.
The bug was caused by miscalculating the index of the child. Since the datasource hadn't updated itself yet I needed to check which index of the child in the Parent-Child structure was still left and add it to the j instead of just the +1
This piece I added after the newItem.quantity == 1 check
//replace the one child that is left and make it basic
int x;
for (x = 1; x<consolidatedItem.quantity; x++)
{
MyCell *cell = (MyCell *)[sender cellForRowAtIndexPath:[NSIndexPath indexPathForRow:x+j inSection:0]];
if (![cell isSelected])
{
break;
}
}
MyCellItem *lastChild = [sender objectAtIndex:j+x];
MyCellItem *newChild = [lastChild copyToConsolidatedStateBasic];
[sender.listOfItems replaceObjectAtIndex:j+x withObject:newChild];
Update: There were in fact lots of problems with this code. Principally the values stored in the NSIndexPaths of the selectedRows array were becoming out of date once a parent row had been deleted from the main array.
Here's the tested and working code (after working with OP)
for (int i = selectedRows.count-1; i >= 0; i--)
{
NSIndexPath *path = [selectedRows objectAtIndex:i];
//check what item you are deleting. act upon the status. Parent- and HoldingCells cant be selected, so the only check that will be made are on child and basic cells
MyCellObject *item = [sender.listOfItems objectAtIndex:path.row];
if (item.consolidatedState == ConsolidationTypeChild)
{
for (int j = path.row; j >= 0; j--)
{
MyCellObject *consolidatedItem = [sender.listOfItems objectAtIndex:j];
if (consolidatedItem.consolidatedState == ConsolidationTypeParent)
{
//copy the consolidated item but with 1 less quantity
MyCellObject *newItem = [consolidatedItem copyWithOneLessQuantity];
if (newItem.quantity > 1)
{
newItem.consolidatedState = ConsolidationTypeParent;
[sender.listOfItems replaceObjectAtIndex:j withObject:newItem];
}
else if (newItem.quantity == 1)
{
MyCellObject *child = [sender.listOfItems objectAtIndex:j+1];
child.consolidatedState = ConsolidationTypeBasic;
[sender.listOfItems replaceObjectAtIndex:j+1 withObject:child];
}
if (newItem.quantity <= 1) {
[sender.listOfItems removeObjectAtIndex:j];
// Update indexPath row values for selected items that are above the removed row
for (int k = i; k >= 0; k--)
{
NSIndexPath *aPath = [selectedRows objectAtIndex:k];
if (aPath.row >= j)
{
[selectedRows replaceObjectAtIndex:k withObject:[NSIndexPath indexPathForRow:(aPath.row -1) inSection:0]];
}
else
{
break;
}
}
path = [NSIndexPath indexPathForRow:(path.row -1) inSection:0]
}
}
}
}
[sender.listOfItems removeObjectAtIndex:path.row];
I have a simple app, I have a button that when pressed will display separate lines of text on the screen. I set an int = 0 and use an if statement that will add 1 at each line. The issue I have is that all lines are displayed after 1 button press, I want 1 button press one line displayed, next button press next line displayed and so on:
int nmbr = 0;
int myInt1 = [textfield.text intValue];
if (nmbr == 0) {
tString = [[NSString alloc] initWithFormat:#"111111111111%i x 1 = %i",myInt1,myInt1];
nmbr++;
}
if (nmbr == 1){
tString1 = [[NSString alloc] initWithFormat:#"222222222222%i x 2 = %i",myInt1,myInt1*2];
nmbr++;
}
if (nmbr == 2){
tString2 = [[NSString alloc] initWithFormat:#"%i x 3 = %i",myInt1,myInt1*3];
nmbr++;
}
I am new to this so any help is greatly appreciated.
Update:
Thanks folks for your help so far, I am not sure that I have explained this issue well……possibly gone about programming it all wrong. I have only one button on screen and each time it is pressed it would change a blank label to a text string. I was incrementing in each if statement so that on the next button press as nmbr = 2 then text label 2 would be modified, press again nmbr = 3 text label 3 would be modified and so on. There appears to be no separation in the code so on the button press all text labels are modified. I am not sure if it is possible to code multiple actions on a single button or if there is a better way to do this.
I have tried else if statements but still no joy.
Move your nmbr++; to the end of the method instead of putting it in each if statement. If you don't want it to increment every time then use a bool to keep track of whether or not you want to increment it. Also, nmbr needs to be a member variable (i.e. class level) instead of method level. If it is method level then its state isn't saved between calls.
Your result is normal because you if test are coming one after each other. The program will go on the first if, incremented nmbr, go out of the if and hit the next if.
It can work by moving the nmbr incrementation out of the "if" (as mentioned by mydogisbox) but you will still have to declare the variable as static.
Something like this:
static int nmbr = 0;
int myInt1 = [textfield.text intValue];
if (nmbr == 0) {
tString = [[NSString alloc] initWithFormat:#"111111111111%i x 1 = %i",myInt1,myInt1];
}
if (nmbr == 1){
tString1 = [[NSString alloc] initWithFormat:#"222222222222%i x 2 = %i",myInt1,myInt1*2];
}
if (nmbr == 2){
tString2 = [[NSString alloc] initWithFormat:#"%i x 3 = %i",myInt1,myInt1*3];
nmbr = 0; // if you have no other lines and you want to start from begining when the user press the button more than 3 times.
}
nmbr++;
Create required number of buttons in Interface Builder, declare them into .h file, create methods for each button -(void)firstButtonClick:(UIButton)sender, -(void)secondButtonClick:(UIButton)sender and put your code into these methods.
The problem is that you're incrementing within the if and then letting it fall to the next. Look at the flow: nmbr is one, so execute that block, which increments nmber to 1, making the next block true also. Depending on your style guides or personal preferences, you could:
Change the later ifs to else if.
Move all the nmbr++s to after the last if.
Return after incrementing inside the if.
Im currently devising a solution for my object BuildingNode *tower which is held inside NSMutableArray *gameObjects, to attack EnemyNode *enemy objects also held inside the gameObjects array.
I have a proposed solution which occasionally causes my game to freeze (temporarily) as the solution employed is quite buggy.
My solution is that each tower object contains its own NSMutableArray *targets which is synthesized from the Tower Class. If an enemy comes into range or out of range of any given tower object, the corrosponding index of the EnemyNode *enemy object from the gameObjects array is saved as an NSNumber into the targets array, or alternatively if the enemy object is out of range, it is removed from the .targets array.
Basically the idea is that the targets array holds the indices of any enemy that is in scope of the tower.
The problem I seem to be facing is that because this tower.targets array is updated dynamically all the time, i believe that if i'm doing some sort of operation on a particular index of tower.targets that then is removed, i get this error:
-[BuildingNode distance]: unrecognized selector sent to instance 0x2dd140
Each BuildingNode *tower has different attacking alogrithms that use the tower.targets array for calling back the sorted/desired enemy.
For example, a random attack style will randomize a number between 0 & [tower.targets count] then I can create a pointer to gameObjects with the corresponding [tower.targets intValue].
Something like this:
EnemyNode *enemy = (EnemyNode *)[gameObjects objectAtIndex:[[tower.targets objectAtIndex:rand]intValue]];
So this will find a random enemy from the potential .targets array and then create a pointer object to an enemy.
I've put in many if statements to ensure that in the case of a .targets index being removed mid-sorting, the operation shouldnt go ahead, thus removing the game failure rate, but it still occurs occassionally.
Heres my code:
Please note that BuildingNode *tower == BuildingNode *build.
This is just a snippet of the iteration inside gameObjects.
//Potential Enemies (Indices) Add and Delete/
if (enemy.distance < build.atk_distance && !enemy.isExploding){
NSNumber *index = [NSNumber numberWithInt:[array indexOfObject:enemy]];
if(![build.targets containsObject:index]){
[build.targets addObject:index];
}
}
else {
NSNumber *index = [NSNumber numberWithInt:[array indexOfObject:enemy]];
if ([build.targets containsObject:index]){
[build.targets removeObject:index];
}
}
}
//Aiming// Nearest Algorithm.
//Will find the nearest enemy to the building
if (enemy.distance < build.atk_distance){
if (!build.isAttacking || build.atk_id == 0){
if ([build.targets count]){
if ([build.atk_style isEqualToString:#"near"]){
int l_dist;
for (int i = 0; i < [build.targets count]; i++){
//Grab the Enemy from Targets
if ([build.targets objectAtIndex:i]){
if([array objectAtIndex:[[build.targets objectAtIndex:i]intValue]]){
EnemyNode *temp = [array objectAtIndex:[[build.targets objectAtIndex:i]intValue]];
if (temp){
int c_dist = temp.distance;
if (!l_dist || c_dist < l_dist){
l_dist = c_dist;
build.atk_id = temp.uniqueID;
build.isAttacking = YES;
}
}
}
}
}
}
}}
Unrecognized selector sent = calling a method on an object that doesn't exist. In this case, you're calling the distance method/property getter on a BuildingNode object.
The only distance I see is on your temp object, which is retrieved by EnemyNode *temp = [array objectAtIndex:[[build.targets objectAtIndex:i]intValue]]; That indicates you are really pulling a BuildingNode object out of the array instead of an EnemyNode. Find out why that happens.