Table cells are empty in a uitableview - objective-c

I have a table with 1 section and many rows. Every cell contains some labels so I want to get their texts. My problem is, with the code below, I cant get the cells after I get first 5 cells. It is strange I know. To be clear, If I have 20 rows I can only get 5 cells with the code below, the rest 15 is null. However the for loop goes 20, First 5 cells are ok but the rest is empty..
All of the rows are same so there is no empty cell. What is my mistake?
NSIndexPath *indexPath;
ReportTableCell *cell;
for (int i = 0; i < [_tableReport numberOfRowsInSection:0] ; i++)
{
indexPath = [NSIndexPath indexPathForRow:i inSection:0];
cell = (ReportTableCell *)[_tableReport cellForRowAtIndexPath:indexPath];
}

That's because the table view doesn't keep cells in the memory that are off the screen. This is an optimisation to reduce memory usage and speed up scrolling.
From the documentation:
cellForRowAtIndexPath:
Return Value An object representing a cell of the table or nil if the cell is not visible or indexPath is out of range.
You can only access the cells that are actually visible at the moment.

UITableView queues the cells for re-use purpose. It means if you have 100 rows, it is not guaranteed that it'll create 100 cells. Typically it'll create only visible cells and later re-use them for rest of the items to show. This is implemented somehow as below:
static NSString *MyCellIdentifier = #"MyCellIdentifier";
UITableViewCell* cell = [tv dequeueReusableCellWithIdentifier:MyCellIdentifier];
//dequeueReusableCellWithIdentifier will give you the cells that has been added to queue after scroll and are ready for re-use.
if(cell == nil){
// create new.
}
So technically you can't get all the cells. Alternative may be applied until you tell the exact purpose you trying to accomplish.

Related

IBAction to display next object of array

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.

how to access the labels using tags in objective-c

I am implementing an iphone application.Which is a UItableview.It has two sections.Three labels are added to all the cells in section-I.I have given tags 1,2,3 for those 3 labels when adding to the cells.Now I want to get the values from label3 from all the cells from section-I and I would like to add all the float values and display the total in another label which is in section-II.I read that I should give different tags for different cell's label3(label with tag-3).How is it possible please guide me.I have float values in the label3.I wanna add all the values and present the result in section-II.Please help me with some reference.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString* curItem = [self.finalItems objectAtIndex:indexPath.row];
NSString* curQuantity = [self.finalPrices objectAtIndex:indexPath.row];
NSString* priceStr = [self fetchDishRecordswithpredicate:curItem];
NSMutableString* priceValStr = [NSMutableString stringWithString:priceStr];
[priceValStr replaceCharactersInRange:NSMakeRange(0, 1) withString:#""];
float totalPrice = [curQuantity floatValue]*[priceValStr floatValue];
UILabel *lblTemp1 = (UILabel *)[cell viewWithTag:1];
UILabel *lblTemp2 = (UILabel *)[cell viewWithTag:2];
UILabel *lblTemp3 = (UILabel *)[cell viewWithTag:3];
switch (indexPath.section) {
case 0: {
lblTemp1.text = curItem;
lblTemp2.text = curQuantity;
lblTemp3.text = [NSString stringWithFormat:#"$ %0.2f", price];
} break;
case 1: {
lblTemp1.text = #"Your Total:";
lblTemp3.text = #"How can I get Total here";
} break;
}
return cell;
}
It should never be necessary to get the value of a label, whether it's part of a table cell or some other kind of view. The user can't modify a label -- they're for display only. That means that the only way that a label can get a given value is for your program to set that value, and that in turn means that you've already got the data you need somewhere. That holds true for any kind of view other than controls that are used for getting input from the user.
The fact that you're looking to get values from labels probably means that you're using your views to store data, which is not a good practice. Views display data, but that data should be stored elsewhere in the program, typically in a data model (assuming you're following the MVC paradigm).
All that said, you can get the cell for a given row from your table using its -cellForRowAtIndexPath: method. From there, you can access the cells subviews using the usual -viewWithTag: method. You definitely don't need a unique set of tags for each cell -- you just need to get the cell for the row that you're interested in. However, keep in mind that a table usually only keeps the cells that are visible around; if you ask it for the cell for a row that's not visible, it'll probably have to create that cell, which in turn will involve asking the table's delegate (your own code!) for the cell. That's a pretty expensive way to ask yourself for data that you already have! ;-)

Moving items from one tableView to another tableView with consolidation [closed]

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];

get value from custom UITableViewCell

I have a custom UITableViewCell that has a UISegmentedControl object. My UITableView (questionnaire form) has 6 of this custom cell (6 segmented controls). I am having trouble getting the selectedSegmentIndex of the segment controls. My guess is that the cells are being released after the table is generated. I am getting the values using the following code:
MyCustomCell *q1 = (MyCustomCell *)[tableView cellForRowAtIndexPath:[NSIndexPath indexPathWithIndex:0]];
int segmentIndex = (int) q1.segmentedController.selectedSegmentIndex;
the int segmentIndex is always giving the same value of 0 no matter what the selected index is.
You must use the right method to initialize your index path for the section and row you want use, otherwise row and section properties won't be correctly set, and you will get the same cell each time :
+(NSIndexPath *)indexPathForRow:(NSUInteger)row inSection:(NSUInteger)section;.
The code looks like :
// getting the cell at third row of first section
NSIndexPath *ip = [NSIndexPath indexPathForRow:2 inSection:0];
MyCustomCell *q1 = (MyCustomCell *)[tableView cellForRowAtIndexPath:ip];
int segmentIndex = (int) q1.segmentedController.selectedSegmentIndex;
You could also consult : NSIndexPath UIKit Additions Reference for more information.

get value of selected row NSTableView

how can i get value of selected row in NSTableView?
By using selectedRow, see here:
#property(readonly) NSInteger selectedRow;
The index of the last selected row (or the last row added to the
selection).
You may also be interested in:
–selectedRowEnumerator (deprecated)
–numberOfSelectedRows
–selectedRowIndexes "returns an index set containing the indexes of the selected rows"
Cocoa follows the Model-View-Controller design pattern, so your reasoning as well as your own designs should as well.
You don't get any values from the table because you already have them in your model. Your controller (usually the data source) asks the table for its selected row index(es), then asks your model for the objects matching those indexes, then does whatever it needs to do.
Assuming that you already have property named tableView and in xib you defined tableView with row cellName
NSInteger row = [tableView selectedRow];
NSTableColumn *column = [tableView tableColumnWithIdentifier:#"cellName"];
NSCell *cell = [column dataCellForRow:row];
NSLog(#"cell value:%#", [cell stringValue]);
To get selected row from table try this..
[tableView selectedRow];
To access the tableView value
[array objectAtIndex:[tableView selectedRow]];
In Swift2.0, this could help you:
func tableViewSelectionDidChange(notification: NSNotification) {
let selectedTableView = notification.object as! NSTableView
print(selectedTableView.selectedRow)
}
As we have all the value in our model, so we can only get the index of row and get the value using index. for e.g:
NSInteger row = [self.nsTableView selectedRow]; //here #property (weak) IBOutlet NSTableView *nsTableView;
NSString *cellValue = ObjectValue[row];// NSMutableArray *ObjectValue;
NSLog(#"%#", cellValue);