I have an array which loads in table view, and if users taps a certain cell it changes to UITableViewCellAccessoryCheckmark.
How can I check what object in array is checked and add all objects that are checked to another array?
If you want a function that actually gets the checked objects at a whim, use the following:
- (NSMutableArray*)checkedObjectsInTable:(UITableView*)tableView
{
NSMutableArray *checkedObjects = [[[NSMutableArray alloc] init] autorelease];
for (int i=0; i<tableDataSource.count; i++)
{
if ([tableView cellForRowAtIndexPath:
[NSIndexPath indexPathForRow:i inSection:0]].accessoryType == UITableViewCellAccessoryCheckmark)
{
[checkedObjects addObject:[tableDataSource objectAtIndex:i]];
}
}
return checkedObjects;
}
That would allow you to get the data on demand. Note that it would be much less efficient than simply using Jasarien's method, yet there are some situations where it is a better solution.
Something like this in your tableView:didSelectRowAtIndexPath: method:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//set checkmark accessory on table cell ...
// get object and add to checkedObjects array
NSInteger index = [indexPath row];
MyObject *object = [myArray objectAtIndex:index];
[checkedObjects addObject:object];
}
Related
I have a uitableview that displays the values of an array. I would like to know if there is a way to update the subtitle of its table cells, based on how many times each cell was tapped.
Thank you!
First of all, you'll want to use a NSMutableArray so you can change its contents after instantiation. Here's a basic over view of what I just tried to achieve your intended results:
In your interface
#property (strong, nonatomic) NSMutableArray *tapCountArray;
In your implementation
- (void)viewDidLoad
{
[super viewDidLoad];
self.tapCountArray = [NSMutableArray new];
int numberOfRows = 20;
for (int i = 0; i < numberOfRows; i ++) {
[self.tapCountArray addObject:#(0)];
}
}
Then the important part!
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.tapCountArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
NSString *text = [self.tapCountArray[indexPath.row] stringValue];
[cell.textLabel setText:text];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.tapCountArray replaceObjectAtIndex:indexPath.row withObject:#([self.tapCountArray[indexPath.row] intValue] + 1)];
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
When each cell is tapped the number in its detailTextLabel will be incremented up by one.
You shouldn't create a new array or set, that could lead to problems if the two arrays get out of sync with each other. The way to do it, as you suggested in your comment, is to use dictionaries. The way you said you were doing that is probably not the way, however. You want an array of dictionaries where the values for one key would be whatever your main data is and the value for the other key would be the number of taps. For example, lets call the two keys main and sub, and your main data is a set of names. The array of dictionaries would look like this: ({main:#"Tom",sub:1}, {main:#"Dick", sub:0}, {main:#"Harry",sub:2}, .....). In the tableView:cellForRowAtIndexPath:indexPath method you would provide the data to the cells like this:
cell.textLabel.text = [[array objectAtIndex:indexPath.row] valueForKey:#"main"];
cell.detailTextLabel.text = [[array objectAtIndex:indexPath.row] valueForKey:#"sub"];
I think you can just set up another array of the same length as the one you have now. Then when your didSelectRowAtIndexPath is triggered, increment your indexPath.row entry of the new array and refresh that cell. If you don't expect to shuffle the table, you don't need a dictionary.
You can insert the object into an NSCountedSet, and on your cellForRowAtIndexPath method, you would take the model object for the cell and verify the number of times it has been inserted into the NSCountedSet instance.
Take a look at the NSCountedSet documentation: https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSCountedSet_Class/Reference/Reference.html
I can't display array in UITableView. The thing I do is - in viewWILLappear I'm creating array. In viewDIDappear I'm filling the array. But when I run [myArr count] or [myArr objectAtIndex:indexPath.row] in table setup I get empty table. If I define constant integer as row count and some constant string as cell text everything works fine. Is there some populate() method I have to run or is it a problem with some order of declarations?
Thanks for any help. Here's the code:
- (void)viewWillAppear:(BOOL)animated {
myArr = [[NSMutableArray alloc] initWithCapacity:100];
}
- (void)viewDidAppear:(BOOL)animated {
[self load_array];
}
- (void) load_array {
for (SomeObject *someObject in SomeObjects) {
[myArr addObject:someObject.someString];
NSLog(#"Value: %#", [myArr objectAtIndex:([myArr count]-1)]); // works
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [myArr count]; // works if I return const ("return 2")
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = [myArr objectAtIndex:indexPath.row]; //=#"ASDF" works.
return cell;
}
You need to perform reloadData on your table view to make the view re-load the table cells.
Update: You should not allocate your array in the viewWillAppear, as this method might be called several times. Construct the array in the viewDidload: and fill it there, or in a background thread, or in the viewWillAppear: (using a conditional statement to check if its already filled). You should also make sure that you do not create a memory leak, from the code you provided it is likely that myArr will be replaced by a newly allocated array without being released.
I have retrieved data from Json URL and displayed it in a table view. I have also inlcuced a button in table view. On clicking the button the data must be transferred to a another table view. The problem is that i could send the data to a view and could display it on a label. But i couldnt bind the dat to table view ... Here's some of the code snippets...
Buy Button...
-(IBAction)Buybutton{
Product *selectedProduct = [[data products]objectAtIndex:0];
CartViewController *cartviewcontroller = [[[CartViewController alloc] initWithNibName:#"CartViewController" bundle:nil]autorelease];
cartviewcontroller.product= selectedProduct;
[self.view addSubview:cartviewcontroller.view];
}
CartView...
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
[super viewDidLoad];
data = [GlobalData SharedData];
NSMutableArray *prod =[[NSMutableArray alloc]init];
prod = [data products];
for(NSDictionary *product in prod){
Cart *myprod = [[Cart alloc]init];
myprod.Description = [product Description];
myprod.ProductImage =[product ProductImage];
myprod.ProductName = [product ProductName];
myprod.SalePrice = [product SalePrice];
[data.carts addObject:myprod];
[myprod release];
}
Cart *cart = [[data carts]objectAtIndex:0];
NSString *productname=[cart ProductName];
self.label.text =productname;
NSLog(#"carts");
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [data.carts count];
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 75;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"cellforrow");
static NSString *CellIdentifier = #"Cell";
ProductCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell ==nil)
{
cell = [[[ProductCell alloc]initWithFrame:CGRectZero reuseIdentifier:CellIdentifier]autorelease];
}
NSUInteger row = [indexPath row];
Cart *cart = [[data carts]objectAtIndex:row];
cell.productNameLabel.text = [cart ProductName];
return cell;
}
I am also getting the following error in the console
2010-06-11 18:34:29.169 navigation[4109:207] *** -[CartViewController tableView:numberOfRowsInSection:]: message sent to deallocated instance 0xcb4d4f90
Your view controller is autoreleased that's why you have the error in the console.
CartViewController *cartviewcontroller =
[[[CartViewController alloc]
initWithNibName:#"CartViewController"
bundle:nil] ***autorelease***];
You should store your view controller in a member variable.
This is depreciated:
cell = [[[ProductCell alloc]initWithFrame:CGRectZero reuseIdentifier:CellIdentifier]autorelease];
Use initWithStyle:reuseIdentifier: instead.
Autorelease isn't a convenience function. It has a specific use. You're only supposed to use it when you are immediately handing an object off to an another external object. Don't use autorelease unless the sending object longer cares if the receiving object lives or dies.
A good rule of thumb is if you ever refer to the receiving object again in the sending object, don't autorelease the receiving object.
Is there a way to hide a UITableView cell? I'm looking for some property or method I can invoke on the UITableViewCell returned by a synchronous cellForRowAtIndexPath() to hide it and make it unselectable by the user.
For me using mapping is not easy way, so I decided to use SAS method. But it doesn't work with my custom cell. So, I correct it:
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(indexPath.row == 7 && hide7Row){
UITableViewCell* cell = [cells objectAtIndex:indexPath.row];
cell.hidden = YES;
return 0.0;
}
else if(indexPath.row == 8 && hide8Row){
UITableViewCell* cell = [cells objectAtIndex:indexPath.row];
cell.hidden = YES;
return 0.0;
}
else {
return 44.0;
}
}
Works Fine.
You mean to leave a gap in the table where the cell should be, or just to progress from the one before it straight to the one after it? In the former case, I guess you might try getting the cell's contentView and set its hidden property to YES; otherwise, you'll just have to do a little logic in your -tableView:numberOfRowsInSection: and -tableView:cellForRowAtIndexPath: methods, returning (the number of cells you'd otherwise return - 1) from the first, and, depending on whether the row index is less than or greater than the row you're not including, either (the cell you'd otherwise return) or (the cell at (the row index + 1)), respectively.
(edit, because the explanation was convoluted:)
- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section
{
if(section == theSectionWithoutARow)
{
if(shouldRemoveTheRow)
return [theArrayWithTheSectionContents count] - 1;
else
return [theArrayWithTheSectionContents count];
}
// other sections, whatever
}
- (UITableViewCell *)tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// blah blah standard table cell creation
id theCellObject;
if(indexPath.section == theSectionWithoutARow)
{
NSInteger theActualRowToDisplay = indexPath.row;
if(shouldRemoveTheRow && indexPath.row >= theRowIndexToRemove)
{
theActualRowToDisplay = indexPath.row + 1;
}
theCellObject = [theArrayWithTheSectionContents objectAtIndex:theActualRowToDisplay];
}
// now set up the cell with theCellObject
return cell;
}
There is no method to do that on the cellForRowAtIndexPath as far as I am aware.
Noah Witherspoon's method seems to be more or less workable, although it will need to be modified if you want multiple rows to be hidden.
Another way to approach it is to create a "cell map", I don't know if this is more efficient or not, but I've used it and it worked.
Let us say you have an NSArray (or mutable version thereof) of data which is to be shown in your TableView. The array's count property is used as the return value for your numberOfRowsInSection delegate method. This is a somewhat typical approach to my knowledge.
To make it so that only some of the rows are shown, I created a "mapping array", which is an NSMutableArray that contains "pointers" to your actual data array. The map contains integers wrapped in NSNumbers. In its virgin state the map's index 0 has the integer 0 (wrapped in NSNumber), index 1 has integer 1, etc.
The UITableView delegate methods are built so that the map's index count is used for numberOfRowsInSection. In the cellForRowAtIndexPath method, it looks at the appropriate index of the map array, retrieves whatever is wrapped in the NSNumber, and then looks in that index of your actual data array.
The benefit of this dereference is that it becomes extremely easy to add and remove cells from the table. Just add/remove the NSNumber objects from your mapping array. Make sense? Sorry, not at my Mac or I could just put up some code samples.
Oh, and don't forget that you have to call the update method (the exact name escapes me) on your TableView so that it refreshes and the cells hide/unhide.
Had the same problem, and as I wanted to avoid some mapping as mentioned, I just set the cell-size to 0:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
NSInteger row=[indexPath row];
float ret=0.0;
if( row==3) {
ret=0.0;
}
else {
ret=40.0;
}
return ret;
}
I used Matt's technique to create a mapping to cell data. Here is some code:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return computeNumberOfRowsAndMapCellData;
}
// Compute mapToCellData to map the index of the cell to the cell data for the
// cell based on TaskConfig show/hide.
// Return the number of rows in section 1.
- (NSInteger)computeNumberOfRowsAndMapCellData {
if (mapToCellData) {
[mapToCellData release];
}
mapToCellData =[[NSMutableArray alloc] init];
if (cellData) {
[cellData release];
}
cellData = [[NSMutableArray alloc] init];
NSInteger numberOfRows = 12; // maximum number of rows
NSNumber *index = [NSNumber numberWithInt:0];
// If the data is not configured to show, decrement the number of rows in the table.
if ( ! [configManager isShowDateForType:task.case_type severity:task.severity]) {
numberOfRows--;
} else {
// Add a map to the cell data with the row number.
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[df stringFromDate:task.respond_due_date], #"Respond Due", nil];
[cellData addObject:dict];
[mapToCellData addObject:index];
int value = [index intValue];
index = [NSNumber numberWithInt:value + 1];
}
// Check the configuration for the rest of the rows of cell data.
....
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
cell.textLabel.numberOfLines = 0;
cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
cell.textLabel.font = [UIFont fontWithName:#"Helvetica-Bold" size:15];
cell.detailTextLabel.font = [UIFont fontWithName:#"Helvetica-Bold" size:14];
}
NSUInteger mapIndex = 0;
// Use the mapToCellData to find cell data based on show/hide in ConfigManager for the data type.
mapIndex = [indexPath row];
NSNumber *cellDataIndex = [mapToCellData objectAtIndex:mapIndex];
NSDictionary *cellDataDict = [cellData objectAtIndex:[cellDataIndex unsignedIntegerValue]];
cell.textLabel.text = = [[cellDataDict allValues] objectAtIndex:0];
cell.detailTextLabel.text = [[cellDataDict allKeys] objectAtIndex:0];
I want to use insertRowsAtIndexPaths method to insert a row to a UITableView, but I don't know how to make "indexPaths" property for this method.
Please help me!
You can create single NSIndexPath using
+ (NSIndexPath *)indexPathForRow:(NSUInteger)row inSection:(NSUInteger)section; // defined in UITableView.h
Your code to create and fill indexPaths paramater may look like:
NSMutableArray* indexPaths = [NSMutableArray arrayWithCapacity:10];
for (...) // iterate to get all indexes and section you need
{
[indexPaths addObject:[NSIndexPath indexPathForRow:someRow inSection:someSection]];
}
//indexPaths is ready to use
- (NSIndexPath *) indexPathForRow:(NSUInteger)row andSection:(NSUInteger)section {
NSUInteger _path[2] = {section, row};
NSIndexPath *_indexPath = [[NSIndexPath alloc] initWithIndexes:_path length:2];
return [_indexPath autorelease];
}