Dynamically Changing UITableView's Contents - iphone

I have an NSURL object which gets data from my site, based on a variable entered by the user into the search bar.
I split this data into an NSArray.
Once I have done that I wish to display the data in a UITableView.
My question is this. Is it possible to load the data into a UITableView dynamically?
i.e. Program loads, no data so UITableView is empty, then the user searches for one variable. Gets some data and the contents is loaded into the UITableView. Searches for a new variable, old data is cleared from UITableView and the new data is added?
I'm currently trying to do this using interface builder, but fear that I may have to make my interface pragmatically, so that I could destroy and re-create the UITableView, but i'm, not sure.
Thanks for any help.

Sure the method reloadData on UITableView will do the trick

Fear not, subclassing UITableView is very easy. In xCode simply choose new file, choose "Cocoa Touch Classes", "Objective-c class" and in the "Subclass of" dropdown pick "UITableView". xCode will add a UITableViewController subclass complete with stubs to build on.
I filled in a very simple example that draws the table data from an array and is displayed from the Application Delegate. As you suggested sending a reloadData message to the UITableView will refresh the displayed data.
As you probably found out, using InterfaceBuilder for this job is a lot harder than doing it programatically.
Cheers, niels
//
// MyTableViewController.m
// TableView
//
// Created by Niels Castle on 7/15/09.
// Copyright 2009 Castle Andersen ApS. All rights reserved.
//
#import "MyTableViewController.h"
#implementation MyTableViewController
// Initializer do custom initialisation here
- (id)initWithStyle:(UITableViewStyle)style {
if (self = [super initWithStyle:style]) {
// This is the source of my data. The simplest source possible,
// an NSMutableArray, of name. This would be the data from your web site
array = [[NSMutableArray alloc]
initWithObjects:#"Niels", #"Camilla", #"William", nil];
}
return self;
}
// How many section do we want in our table
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view
// Simply the number of elements in our array of names
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
return [array count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Reuse cells
static NSString *id = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:id];
if (cell == nil) {
cell = [[[UITableViewCell alloc]
initWithStyle: UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
}
// Simplest possible cell - displaying a name from our name array
[[cell textLabel] setText: [array objectAtIndex:[indexPath row]]];
return cell;
}
- (void)dealloc {
[super dealloc];
[array release];
}
#end
//
// TableViewAppDelegate.m
// TableView
//
// Created by Niels Castle on 7/15/09.
// Copyright Castle Andersen ApS 2009. All rights reserved.
//
#import "TableViewAppDelegate.h"
#import "MyTableViewController.h"
#implementation TableViewAppDelegate
#synthesize window;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
MyTableViewController *twc = [[MyTableViewController alloc]
initWithStyle: UITableViewStylePlain];
[window addSubview: [twc view]];
[window makeKeyAndVisible];
}
- (void)dealloc {
[window release];
[super dealloc];
}
#end

It's a bit complicated, but my solution that is working very reliably is following:
(assume that you have an array as bunch of arrays, that each represents a section and contains items that are in fact rows in table).
This example fits at the situation, when we load some data from server (e.g. JSON), and the result can be very different in number of sections and/or rows.
void function, you can omit it
-(void)addToPropertiesTable {
//fullTableData is above mentioned two dimensional array
int sectionsCount = _fullTableData.count;
int count = 0;
NSMutableArray *insertIndexPaths = [NSMutableArray array];
NSMutableArray *deleteIndexPaths = [NSMutableArray array];
for(int j = 0; j < sectionsCount; j++) {
NSMutableArray *currentAdverts = [[NSMutableArray alloc] init];
[currentAdverts addObjectsFromArray:[_fullTableData objectAtIndex:j]];
count = [currentAdverts count];
int currentRowsInSection = [self.propertiesTable numberOfRowsInSection:j];
if(currentRowsInSection > 0) {
//if any data in current tableView, lets get rid of them first
for (int i = [self.propertiesTable numberOfRowsInSection:j] - 1; i >=0 ; i--)
{
[deleteIndexPaths addObject:[NSIndexPath indexPathForRow:i inSection:j]];
}
}
for (NSUInteger item = 0; item < count; item++) {
[insertIndexPaths addObject:[NSIndexPath indexPathForRow:item inSection:j]];
}
}
[self.propertiesTable beginUpdates];
//we delete old rows - whether we need them or not
[self.propertiesTable deleteRowsAtIndexPaths:deleteIndexPaths
withRowAnimation:UITableViewRowAnimationFade];
if([self.propertiesTable numberOfSections]) {
//if any sections, we remove them
NSIndexSet *nsIndexSetToDelete = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0,[self.propertiesTable numberOfSections])];
[self.propertiesTable deleteSections:nsIndexSetToDelete withRowAnimation:UITableViewRowAnimationAutomatic];
}
//here we have to set new sections, whether they have changed or not
NSIndexSet *nsIndexSetToInsert = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0,sectionsCount)];
[self.propertiesTable insertSections:nsIndexSetToInsert withRowAnimation:UITableViewRowAnimationAutomatic];
//finally we insert rows
[self.propertiesTable insertRowsAtIndexPaths:insertIndexPaths
withRowAnimation:UITableViewRowAnimationFade];
[self.propertiesTable endUpdates];
//now we see the change in UI
[self.propertiesTable reloadData];
}

Related

iOS: dynamically adding data from UITableView to NSArray

I have a UITableView in which I have a custom prototype cell, defined in another class (CustomCell), with a UITextField in it. Every time I press a button, it calls a method called addItem, which creates a new cell. I want the texts in the UITextFields to go to an array. To try to explain it better, if I add 3 cells to the UITableView and input 3 texts in the corresponding UITextFields, I want the text in 1st cell to go to the array in index 0, the text in the 2nd to go to index 1 and the text in 3rd cell to go to index 2. My biggest problem is that I want to be able to go back to UITextField in cell 1 and update it, and have it dynamically update the NSArray object corresponding to it, that is, the one at index 0. I have no idea how to approach it. Can anybody help???
Thank you very much!!
my code (obs: itemTable is the UITableView):
MainViewController.m
#implementation addViewController{
NSInteger n;
NSString *aid;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
-(void)viewWillAppear:(BOOL)animated{
n=1;
aid=#"";
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return n;
}
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier= #"Cell";
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
}
cell.itemNumber.text=[NSString stringWithFormat:#"Item %d",indexPath.row];
return cell;
}
- (IBAction)addItem:(UIButton *)sender {
++n;
[_itemTable reloadData];
}
- (IBAction)removeItem:(UIButton *)sender {
if (n>=0)--n;
[_itemTable reloadData];
}
CustomCell.m:
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {_itemValue = [[UITextField alloc]init];
_item = [[UILabel alloc]init];
[self.contentView addSubview:_itemValue];
[self.contentView addSubview:_item];
}
return self;
}
CustomCell.h
#interface CustomCell : UITableViewCell
#property (strong, nonatomic) IBOutlet UILabel *itemNumber;
#property (strong, nonatomic) IBOutlet UITextField *itemValue;
#end
First, when you create each text field, you make yourself that text field's delegate, so you will get messages whenever something happens in the text field.
Okay, so now when the user types in a text field, you will get messages, and you can modify your model (the array, which you should keep as an NSMutableArray I suppose). But to do that, you need to figure out which the heck cell contains the text field that this message is coming from! You will do that something like this:
- (void)textFieldDidEndEditing:(UITextField *)tf {
// some cell's text field has finished editing; which cell?
UIView* v = tf;
do {
v = v.superview;
} while (![v isKindOfClass: [UITableViewCell class]]);
CustomCell* cell = (CustomCell*)v;
// so which row is it?
NSIndexPath* ip = [self.tableView indexPathForCell:cell];
// aha! so now ip.row is the row, and you can update your data model
// ... left as an exercise for the reader ...
}
I do exactly this sort of thing in my book, in http://www.apeth.com/iOSBook/ch21.html#_editable_content_in_table_items (that's where the above code comes from), so take a look and see what ideas it gives you.
When the user is done entering text you could do something like the following which maps the index paths of the rows in your tableview to the indices in an array.
- (NSMutableArray *)updateText {
NSUInteger cellsCount = [self.tableView numberOfRowsInSection:0];
NSMutableArray *cellTextArray = [[NSMutableArray alloc] initWithCapacity:cellsCount];
for(NSInteger i = 0; i < cellsCount; i++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
CustomCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
NSString *item = cell.itemNumber.text;
[cellTextArray insertObject:item atIndex:i];
}
return cellTextArray;
}
Assuming your cell has the UITextFieldDelegate set, when the user is done entering text you can do something like this:
- (void)textFieldDidEndEditing:(UITextField *)textField {
[self.delegate didFinishEditing];
}
Where self.delegate is the UITableViewController, which in turn call updateText when necessary.
Things to be careful of - the for loop in updateText needs to loop over the tableview and dequeue cells for each index path. Simply using the tableview's visible cells would most likely leaving you missing text from cells that were off screen and got reused.
Hope this helps and good luck!
There are obviously a few aspects of this problem. First of all, you want to be able to recover references to the UILabel's, so that you can figure out which row a specific UILabel is in. I'd recommend doing this using the tag property, like this:
_item = [[UILabel alloc] init];
_item.tag = 100; // or any value
[self.contentView addSubview:_item];
You also need to set an action that gets called whenever the text in the label gets changed. You can do that like this:
[_item addTarget:someObject
action:#selector(labelChanged:)
forControlEvents:UIControlEventEditingChanged];
Whatever class someObject is, it needs to have a method with this signature:
- (void)labelChanged:(id)sender;
Inside that method you can check that sender is in fact a UILabel, and then you can access the new text with sender.text.
In order to figure out what point in the array to put the text in, you can declare a loop over the number of rows in your table:
for (int i = 0; i < [mainViewControllerInstance tableView:theTableInQuestion numberOfRowsInSection:0]; ++i) {
if(sender == [[mainViewControllerInstance tableView:theTableInQuestion
cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]] viewWithTag:100]) {
// Put `sender.text` in the appropriate spot in your array
}
}
A few additional notes: I'd use an NSMutableArray to keep track of your strings, since you'll be updating them, but I'm not entirely sure what best practices are here. You'll also want to make sure you initialize your array (whether you make it an NSArray or an NSMutableArray) to have the proper number of rows, and make sure that you add rows to it when the + button is pressed, or you'll risk getting an exception when you try to change an entry for a row that doesn't yet exist.
You might also want to have a look at the free Sensible TableView framework. The framework performs almost all what you need automatically. Should probably save you a lot of manual work. Good luck!

reload tableview after call to WCF service

I am calling a WCF service to display data in a UITableViewController.The code in the .m file is:
- (void)viewDidLoad
{
[super viewDidLoad];
[docTable setDataSource:self];
[docTable setDelegate:self];
}
-(void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
EDViPadDocSyncService *service = [[EDViPadDocSyncService alloc]init];
EDVCategory *cat = [EDVCategory alloc];
cat.categoryId = [catId intValue];
[service getDocsByCatId:self action:#selector(getDocsByCatIdHandler:) category:cat];
[docTable reloadData];
}
- (void) getDocsByCatIdHandler: (id)value
{
if([value isKindOfClass:[NSError class]])
{
NSLog(#"%#", value);
return;
}
if([value isKindOfClass:[SoapFault class]])
{
NSLog(#"%#", value);
return;
}
NSMutableArray* result = (NSMutableArray*)value;
NSMutableArray *documentList = [[NSMutableArray alloc] init];
self.myDocList = [[NSMutableArray array] init];
for (int i = 0; i < [result count]; i++)
{
EDVDocument *docObj = [[EDVDocument alloc]init];
docObj = [result objectAtIndex:i];
[documentList addObject:[docObj docName]];
}
self.myDocList = documentList;
[docTable reloadData];
}
- (void)viewDidUnload
{
[super viewDidUnload];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
int cnt = [self.myDocList count];
NSLog(#"ABC=%#",cnt);
return [self.myDocList count];
//return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
DocumentCell *cell = [tableView dequeueReusableCellWithIdentifier:#"DocumentCell"];
if (cell == nil)
{
cell = [[[DocumentCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"DocumentCell"] autorelease];
}
NSLog(#"cell text=%#",[self.myDocList objectAtIndex:indexPath.row]);
cell.lblDocName.text = [self.myDocList objectAtIndex:indexPath.row];
return cell;
}
I am using storyboard.I have hooked the "docTable",set the datasource and the delegate for "docTable".The problem is,the service is called after the call to "numberOfRowsInSection".So,'return [self.myDocList count]' is 0.I have put [docTable reloadData] in viewWillAppear as well as in the service handler,that is,"getDocsByCatIdHandler".But it isn't getting reloaded,as expected.Is there anything else I can try? EDIT:- This a Master-Detail application.I have used the same code for loading data in the "MasterViewController" UITableViewController and it works.When the user selects a cell in this table,I need to populate data in the second tableview by calling the WCF service.The second tableview isn't displaying data.
Everything looks good which leads me to believe you are not getting the results from the webservice you are expecting.
One small thing first thats not your problem. If result is in fact an array and there is an object in it, you shouldnt need to alloc a new EDVDocument.
EDVDocument *docObj = [result objectAtIndex:i];
Can you log the (id)value parameter to see what we're working with?
NSLog(#"%#", value);
If value is not an array, the cast wont complain, it will just work by not working. However, if it is an array you may be finding trouble with assigning your property (granted I dont know how its declared) to a local array. You can use the following function to create a new array with the elements of your temporary array;
self.myDocList = [[NSArray alloc] initWithArray:documentList];
[docTable reloadData];
I hope this helps.
I was facing the same issue when i have a async webService call. I was using a private Library to call webservice so my control goes to the library and after the response comes a method in Appdelegate is set as handler. So what you need to do is before calling the Webservice save the state of tableview in a shared variable and after you have received response set it back to tableView and then call the reload method. Something like below:
SharedView.tblView = self.tableView;
[webservice Call];
After Response:
self.tableView = SharedView.tblView;
[self.tableView reloadData];
Hope This Helps.

How to implement sections in table view containing SQLite database?

So, i have UITableView, large amount of data which is displayed with a many rows, and i want to make sections (like default contacts application and its sections). So there is my code (listViewController.m file):
#import "FailedBanksListViewController.h"
#import "FailedBankDatabase.h"
#import "FailedBankInfo.h"
#import "FailedBanksDetailViewController.h"
#import "BIDAppDelegate.h"
#implementation FailedBanksListViewController
#synthesize failedBankInfos = _failedBankInfos;
#synthesize details = _details;
- (void)viewDidLoad {
self.view.backgroundColor=[UIColor colorWithPatternImage:[UIImage imageNamed:#"3.png"]];;
[super viewDidLoad];
self.failedBankInfos = [FailedBankDatabase database].failedBankInfos;
self.title = #"Продукты";
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.failedBankInfos = nil;
self.details = nil;
}
- (void) viewWillAppear:(BOOL)animated
{
}
#pragma mark Table view methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [_failedBankInfos count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [_failedBankInfos count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
// Set up the cell...
FailedBankInfo *info = [_failedBankInfos objectAtIndex:indexPath.row];
cell.textLabel.text = info.name;
cell.detailTextLabel.text = [NSString stringWithFormat:#"%#, %#", info.city, info.state];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (self.details == nil) {
self.details = [[FailedBanksDetailViewController alloc] initWithNibName:#"FailedBanksDetailViewController" bundle:nil];
}
FailedBankInfo *info = [_failedBankInfos objectAtIndex:indexPath.row];
_details.uniqueId = info.uniqueId;
[self.navigationController pushViewController:_details animated:YES];
}
- (void)dealloc {
self.failedBankInfos = nil;
}
#end
With your code you should have multiple sections (each one exactly equal than the others).
The idea for a multiple section table view is (normally) to have a 2 dimensional array (not 1 dimensional as is your case). Then each row would represent a section for your table view.
For example, if you have an array structured this way (and I know you can't initialize it this way):
arr = {
{'apple','orange','banana'},
{'CD-Rom', 'DVD', 'BR-Disk'},
{'AK-47', 'Rocket launcher', 'Water gun'}
}
your number of sections method may return [arr count] and the number of rows for section s may return [[arr objectAtIndex:s] count]. And remember that you can set the title for each section with the table view datasource method tableView:titleForHeaderInSection:.
If you want to load the info from a SQLite DB, nothing may change. It's exactly the same but you will have to keep of the way to get your data.
When you thing you understand all this stuff then checkout the Core Data framework.

how to properly use insertRowsAtIndexPaths?

I went through all the examples online and could not figure out how to properly add a cell to a tableview with animation. Let's say I have one section with one cell and I want to add another cell once the user clicks on the first cell's accessory.
My "add" method does this:
- (IBAction) toggleEnabledTextForSwitch1onSomeLabel: (id) sender {
if (switch1.on) {
NSArray *appleComputers = [NSArray arrayWithObjects:#"WWWWW" ,#"XXXX", #"YYYY", #"ZZZZ", nil];
NSDictionary *appleComputersDict = [NSDictionary dictionaryWithObject:appleComputers forKey:#"Computers"];
[listOfItems replaceObjectAtIndex:0 withObject:appleComputersDict];
[tblSimpleTable reloadData];
}
Which is working but there is no animation. I understand that in order to add animation, I need to use insertRowsAtIndexPaths:withRowAnimation, so I tried tons of options but it always crashes when executing the insertRowsAtIndexPaths:withRowAnimation method.
My recent try was by doing this:
- (IBAction) toggleEnabledTextForSwitch1onSomeLabel: (id) sender {
if (switch1.on) {
NSIndexPath *path1 = [NSIndexPath indexPathForRow:1 inSection:0]; //ALSO TRIED WITH indexPathRow:0
NSArray *indexArray = [NSArray arrayWithObjects:path1,nil];
[tblSimpleTable insertRowsAtIndexPaths:indexArray withRowAnimation:UITableViewRowAnimationRight];
}
}
What am I doing wrong? How can I make this happen easily? I dont understand this whole indexPathForRow thing...I also dont understand how with this method I can add a label name to the new cell. Please help...thanks!!
It's a two step process:
First update your data source so numberOfRowsInSection and cellForRowAtIndexPath will return the correct values for your post-insert data. You must do this before you insert or delete rows or you will see the "invalid number of rows" error that you're getting.
Then insert your row:
[tblSimpleTable beginUpdates];
[tblSimpleTable insertRowsAtIndexPaths:indexArray withRowAnimation:UITableViewRowAnimationRight];
[tblSimpleTable endUpdates];
Simply inserting or deleting a row doesn't change your data source; you have to do that yourself.
The important thing to keep in mind when using insertRowsAtIndexPaths is that your UITableViewDataSource needs to match what the insert is telling it to do. If you add a row to the tableview, make sure the backing data is already updated to match.
First of all, you should update your data model just before update table itself.
Also you can use:
[tableView beginUpdates];
// do all row insertion/delete here
[tableView endUpdates];
And table will produce all changed at once with animation (if you specify it)
The insertRowsAtIndexPaths:withRowAnimation: AND the changes to your data model both need to occur in-between beginUpdates and endUpates
I've created a simple example that should work on its own. I spent a week fiddling around trying to figure this out since I couldn't find any simple examples, so I hope this saves someone time and headache!
#interface MyTableViewController ()
#property (nonatomic, strong) NSMutableArray *expandableArray;
#property (nonatomic, strong) NSMutableArray *indexPaths;
#property (nonatomic, strong) UITableView *myTableView;
#end
#implementation MyTableViewController
- (void)viewDidLoad
{
[self setupArray];
}
- (void)setupArray
{
self.expandableArray = #[#"One", #"Two", #"Three", #"Four", #"Five"].mutableCopy;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.expandableArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//here you should create a cell that displays information from self.expandableArray, and return it
}
//call this method if your button/cell/whatever is tapped
- (void)didTapTriggerToChangeTableView
{
if (/*some condition occurs that makes you want to expand the tableView*/) {
[self expandArray]
}else if (/*some other condition occurs that makes you want to retract the tableView*/){
[self retractArray]
}
}
//this example adds 1 item
- (void)expandArray
{
//create an array of indexPaths
self.indexPaths = [[NSMutableArray alloc] init];
for (int i = theFirstIndexWhereYouWantToInsertYourAdditionalCells; i < theTotalNumberOfAdditionalCellsToInsert + theFirstIndexWhereYouWantToInsertYourAdditionalCells; i++) {
[self.indexPaths addObject:[NSIndexPath indexPathForRow:i inSection:0]];
}
//modify your array AND call insertRowsAtIndexPaths:withRowAnimation: INBETWEEN beginUpdates and endUpdates
[self.myTableView beginUpdates];
//HERE IS WHERE YOU NEED TO ALTER self.expandableArray to have the additional/new data values, eg:
[self.expandableArray addObject:#"Six"];
[self.myTableView insertRowsAtIndexPaths:self.indexPaths withRowAnimation:(UITableViewRowAnimationFade)]; //or a rowAnimation of your choice
[self.myTableView endUpdates];
}
//this example removes all but the first 3 items
- (void)retractArray
{
NSRange range;
range.location = 3;
range.length = self.expandableArray.count - 3;
//modify your array AND call insertRowsAtIndexPaths:withRowAnimation: INBETWEEN beginUpdates and endUpdates
[self.myTableView beginUpdates];
[self.expandableArray removeObjectsInRange:range];
[self.myTableView deleteRowsAtIndexPaths:self.indexPaths withRowAnimation:UITableViewRowAnimationFade]; //or a rowAnimation of your choice
[self.myTableView endUpdates];
}
#end
For the swift users
// have inserted new item into data source
// update
self.tableView.beginUpdates()
var ip = NSIndexPath(forRow:find(self.yourDataSource, theNewObject)!, inSection: 0)
self.tableView.insertRowsAtIndexPaths([ip], withRowAnimation: UITableViewRowAnimation.Fade)
self.tableView.endUpdates()

Can't populate cells with an array when I have loaded a second UITableViewController

I'm very new to iPhone programming, I'm creating my first app, (a world cup one)
the first view is a table view. the cell text label is filled with an array, so it shows all the groups (group a, B, c,etc) then when you select a group, it pulls on another UITableViewcontroller, but whatever I do I cant set the text label of the cells (e.g france,mexico,south africa, etc. in fact nothing I do to the cellForRowAtIndexPath makes a difference, could someone tell me what I'm doing wrong please
Thanks
Here is my code for the view controller:
GroupADetailViewController.m
#import "GroupADetailViewController.h"
#implementation GroupADetailViewController
#synthesize groupLabel = _groupLabel;
#synthesize groupADetail = _groupADetail;
#synthesize teamsInGroupA;
#pragma mark Memory management
- (void)dealloc {
[_groupADetail release];
[_groupLabel release];
[super dealloc];
}
#pragma mark View lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
// Set the number label to show the number data
teamsInGroupA = [[NSArray alloc]initWithObjects:#"France",#"Mexico",#"Uruguay",#"South Africa",nil];
NSLog(#"loaded");
// Set the title to also show the number data
[[self navigationItem]setTitle:#"Group A"];
//[[self navigationItem]cell.textLabel.text:#"test"];
//[[self navigationItem] setTitle[NSString String
}
- (void)viewDidUnload {
[self setgroupLabel:nil];
}
#pragma mark Table view methods
- (NSInteger)numberOfSectionsInTableView:(UITableView*)tableView {
// Return the number of sections in the table view
return 1;
}
- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in a specific section
// Since we only have one section, just return the number of rows in the table
return 4;
NSLog:("count is %d",[teamsInGroupA count]);
}
- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath {
static NSString *cellIdentifier2 = #"Cell2";
// Reuse an existing cell if one is available for reuse
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier2];
// If no cell was available, create a new one
if (cell == nil) {
NSLog(#"no cell, creating");
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier2] autorelease];
[cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
}
NSLog(#"cell already there");
// Configure the cell to show the data for this row
//[[cell textLabel]setText:[NSString string
//[[cell textLabel]setText:[teamsInGroupA objectAtIndex:indexPath.row]];
//NSUInteger row = [indexPath row];
//[cell setText:[[teamsInGroupA objectAtIndex:indexPath:row]retain]];
//cell.textLabel.text:#"Test"
[[cell textLabel]setText:[teamsInGroupA objectAtIndex:indexPath.row]];
return cell;
}
#end
GroupADetailViewController.m
#import "GroupADetailViewController.h"
#implementation GroupADetailViewController
#synthesize groupLabel = _groupLabel;
#synthesize groupADetail = _groupADetail;
#synthesize teamsInGroupA;
#pragma mark Memory management
- (void)dealloc {
[_groupADetail release];
[_groupLabel release];
[super dealloc];
}
#pragma mark View lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
// Set the number label to show the number data
teamsInGroupA = [[NSArray alloc]initWithObjects:#"France",#"Mexico",#"Uruguay",#"South Africa",nil];
NSLog(#"loaded");
// Set the title to also show the number data
[[self navigationItem]setTitle:#"Group A"];
//[[self navigationItem]cell.textLabel.text:#"test"];
//[[self navigationItem] setTitle[NSString String
}
- (void)viewDidUnload {
[self setgroupLabel:nil];
}
#pragma mark Table view methods
- (NSInteger)numberOfSectionsInTableView:(UITableView*)tableView {
// Return the number of sections in the table view
return 1;
}
- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in a specific section
// Since we only have one section, just return the number of rows in the table
return 4;
NSLog:("count is %d",[teamsInGroupA count]);
}
- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath {
static NSString *cellIdentifier2 = #"Cell2";
// Reuse an existing cell if one is available for reuse
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier2];
// If no cell was available, create a new one
if (cell == nil) {
NSLog(#"no cell, creating");
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier2] autorelease];
[cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
}
NSLog(#"cell already there");
// Configure the cell to show the data for this row
//[[cell textLabel]setText:[NSString string
//[[cell textLabel]setText:[teamsInGroupA objectAtIndex:indexPath.row]];
//NSUInteger row = [indexPath row];
//[cell setText:[[teamsInGroupA objectAtIndex:indexPath:row]retain]];
//cell.textLabel.text:#"Test"
[[cell textLabel]setText:[teamsInGroupA objectAtIndex:indexPath.row]];
return cell;
}
#end
Are you setting a delegate properly on the second UITableView? Is it being loaded from an xib or are you just hard-coding it? Also, I don't see didSelectCellForIndexPath: anywhere. Is the second tableView even loading on screen?
Your tableViewController must set the delegate property to an instance that conforms to UITableViewDelegate and a dataSource property to an object that conforms to UITableViewDataSource protocol. Off course those properties can be set to self:
you can insert this
self.dataSource = self;
self.delegate = self;
in your viewDidLoad method.
If you don't do that the required methods (defined in protocols) you implemented are never called because the two properties are nil by default.
Does it fix your problem ?
This cocoapod https://github.com/xmartlabs/XLData makes the UITableView/UICollectionView loading much more easier than dealing directly with dataSource and delegates.