Manage tableview Cells from already stored data - ios

Hi I am trying to make table view that has 5 cell and each cell automatically fills from the data or data type I have stored. here is the an example of what I have in mind, Please let me know if my question wasn't clear enough
cheers.
Maybe I asked my question in a wrong way, I'll try to explain it more.
So the Application I am making works this way:
I have 5 different Drill type that each has different condition, and I need to make a different drill recording stats (UI) and tableView for each drill type,  
So in the first table view that call Drills I have a cell that should show drill type and date that user made also one Create drill button that takes the user  to create drill (UI ), that has some attributes in it for example name, date , and DrillType(only 5 option),  
After this I have to make 5 different table and scoringViewControl for each drill type specifically that they be able to insert their stats as many times as they want.
And here I have problem, I don’t know what is the best way to manage navigating my cells in to the correct [drilltype] tableview..
(if I didn’t have to have some condition in create drill I would just made 5 statics Cell and navigate them into different direction)
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if([self.drill.drillType isEqual: #"Grouping"] ){
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
Drill *drill = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = drill.drillType;
cell.detailTextLabel.text = drill.drillDate;
return cell;
}
else if([self.drill.drillType isEqual: #"Normal"] ){
static NSString *CellIdentifier = #"Cell2";
UITableViewCell *cell2 = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
Drill *drill = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell2.textLabel.text = drill.drillType;
cell2.detailTextLabel.text = drill.drillDate;
return cell2;
}
return 0;
}

If you need want to have many cells you should use dynamic dispatch. Use dictionary to map type of cell to the key (if statement). Here is code
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *cellIds = #{#"Grouping" : #"GroupingCellId", #"Normal" : #"NormalCellId"};
Drill *drill = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSString *CellIdentifier = cellIds[drill.drillType];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
[self configureCell:cell withObject:drill];
return cell;
}
Also if you need to do some specific configuration for cells you could do it in 2 ways:
1 - Subclass a UITableViewCell and implement method that does cells configuration. different cells will implement this method in different ways. This technic is called "Polymorphism". Some explanation
[cell setDrill:drill]
2 - Make a dynamic dispatch (call a method) depending on what type is it. The same tech nick as for getting different cells for different type.
NSDictionary *configMethods = #{#"Grouping" : #"configGrouping", #"Normal" : #"configNormal"};
NSString *method = configMethods[drill.drillType];
[self performSelector:NSSelectorFromString(method) withObject:cell withObject:drill];

Related

Obj-c - Return two custom cells at the same time

I have two different custom TableView Cells. That said, when the first cell type is returned, I want the other returned immediately after. Technically, the below code is what I want to occur:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
ChatTableViewCell *cell = (ChatTableViewCell *)[tableView dequeueReusableCellWithIdentifier:ChatTableIdentifier forIndexPath:indexPath];
UniversalAlertTableViewCell *cellUni = (UniversalAlertTableViewCell *)[tableView dequeueReusableCellWithIdentifier:ChatTableIdentifierUni forIndexPath:indexPath];
return cell;
return cellUni;
}
However, as we know, code stops executing after the first return cell. How can I accomplish this?
You can only return one specific type at a time. Use the indexPath parameter to decide which one to return.
The following code is a rough idea of what you need. It's up to you to adapt the if statement based on your actual needs.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 0) {
ChatTableViewCell *cell = (ChatTableViewCell *)[tableView dequeueReusableCellWithIdentifier:ChatTableIdentifier forIndexPath:indexPath];
// configure cell based on data from your data model for this indexPath
return cell;
} else {
UniversalAlertTableViewCell *cell = (UniversalAlertTableViewCell *)[tableView dequeueReusableCellWithIdentifier:ChatTableIdentifierUni forIndexPath:indexPath];
// configure cell based on data from your data model for this indexPath
return cell;
}
}
Your question is very interesting. First clarify your needs.
I guess your demand is probably like this, and once the layout is displayed, switch to another layout. Let me talk about my habit of using tables. I am used to using data to control display and animation behavior. For your needs, I can use two data sources, corresponding to two kinds of cells. After one data source is displayed, immediately switch to another data source, and finally refresh the table.
The code is probably like this, you need a data source array models, and then each cell corresponds to a data source.
#property (strong, nonatomic) NSArray *models;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
ChatModel *model = ChatModel.new;
self.models = #[model];
[self.tableView reloadData];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
UniversalAlertModel *universalModel = UniversalAlertModel.new;
self.models = #[universalModel];
[self.tableView reloadData];
});
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return self.models.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
id model = self.models[indexPath.row];
if ([model isMemberOfClass:ChatModel.class]) {
ChatTableViewCell *cell = (ChatTableViewCell *)[tableView dequeueReusableCellWithIdentifier:ChatTableIdentifier forIndexPath:indexPath];
// Set UI display according to model
return cell;
}else if ([model isMemberOfClass:UniversalAlertModel.class]){
UniversalAlertTableViewCell *cellUni = (UniversalAlertTableViewCell *)[tableView dequeueReusableCellWithIdentifier:ChatTableIdentifierUni forIndexPath:indexPath];
// Set UI display according to model
return cellUni;
}
}
You cannot return two custom cells at the same time. It is not possible, because the delegate only runs one time per row. You can return a different cell on a different row or section.

iOS scrolling tableView reusable cells reload

I am working with tableviews in iOS. Reusable cells are reloaded when scrolling.
So, when updating -for example- textfields inside a cell, it disappears once scrolling over. I solved it by using an Array that saves all texts in all cells, but I wonder if there's a better way to solve this issue.
Thanks.
Using String array you have to store data of all textfields in tableview.
Use delegate methods of UITableView to implement more efficiently.
-(void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
MyCell * mc = (MyCell *) cell;
names[indexPath.row] = mc.myTf.text;
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
MyCell * mc = (MyCell *) cell;
mc.myTf.text = names[indexPath.row];
}
Here, MyCell is the custom cell which has UITextField. name[] is the NSString array declared at class scope like NSString * names[20].
What you are describing is how tableviews are meant to work! The tableView is a display, not a store, and the reusable cells are only those currently displayed
You are right to use an array (or collection) to hold the data and just use the tableView to display it
for memory management (to releasing memory) tableview remove cell memory which are not displaying in current screen its only keep those cell in memory which are currently displaying on screen so you have to store that text separate from tableview.
You issue can be because of multithreading. If you are reloading data from a GCD operation or any NSOperation(different thread), then you have to use the below code to get the handle to main thread
dispatch_sync(dispatch_get_main_queue(), ^{
//perform the reload activity
});
Yes you are doing right, as cells are reused you need to save your data, something like this -
1 . Have an array to hold your data.
2 . Update your array whenever you make any change in your textFields, so that your UI and data are in sync.
Use this array to populate your tableView.
You need to define the cellIdentifier as unique. Then each cell created with unique identifier.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = [NSString stringWithFormat:#"Cell%dR%d",indexPath.section,indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
//your logic here
}
return cell;
}

how to not return a UITableviewCell

I have a NSArray of NSDictionaries, in this array there are several values which I do not want to show in the UITableView, I would like to know how to avoid returning these cells in the tableView:cellForRowAtIndexPath:
I have tried to return nil; but this has caused me errors.
This is what my code looks like
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
CustomInstallCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[CustomInstallCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
cell.selectionStyle = UITableViewCellSelectionStyleGray;
currentInstallDictionary = [sortedItemsArray objectAtIndex:indexPath.row];
NSNumber *tempDP = [currentInstallDictionary objectForKey:#"dp"];
NSInteger myInteger = [tempDP integerValue];
if (myInteger == 0) {
return cell;
}
return nil; // gives error
}
any help would be appreciated.
This method must return a cell. It cannot return nil. The best thing to do is filter your list before you load your table and use the filtered array when dequeueing cells.
The UITableView is only asking for a cell because you told it to ask. Your implementation of the UITableViewDataSource protocol implements the two methods:
numberOfSectionsInTableView:
tableView:numberOfRowsInSection:
In those methods you determine how many cells should appear on screen. As Brian Shamblen answered, if you don't want that data to appear in the table view, some how ignore (filter, delete, whatever) that data when you calculate the number of sections and rows. If you do so, no "extra" cells will be requested.

UITableView Static Scrolling

I have a simple UITableView using custom UITableViewCells.
The options set on the UITableView's properties are only that the style is set to Grouped.
When I'm trying to scroll down through the different items the scroll is extremely jumpy.
I've researched this quite a bit looking at Tricks for improving iPhone UITableView scrolling performance? and a few other questions on this website. I haven't really been able to find a solution though.
EDIT ****
I use a WSDL webservice to load data into the UITableViewCells.
The cells only have a UITextView and three buttons in it.
EDIT ****
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"NavigatorCell";
NewCell *cell = (NewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"NewCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
cell.postId = [[items objectAtIndex:indexPath.row] objectForKey:#"PostID"];
cell.post.text = [[items objectAtIndex:indexPath.row] objectForKey:#"Post"];
return cell;
}
I see your NewCell is subclassed.
Don't forget to include this method into your NewCell.m
- (NSString *) reuseIdentifier
{
return #"Cell Identifier";
}
Of course #"Cell Identifier" should be the same that you use in your cellForRowAtIndexPath:.
If you fail to implement this method each cell will be generated from scratch.
Are you using a dequeReusableCellWithIdentifier? Follow the format below. Since you now mention you are loading data from the web you need to do this asynchronously to allow for smooth scrolling. To load from a webservice (asynchronously) there is a nice project just for that here
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"yourCellName";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
return cell;
}
Setting your tableview to Reuse cells is the most basic way to ensure good performance. Basically it means that instead of creating a new cell for every cell in the tableview, your tableview will recycle the cells that are off screen. The basic setup is below, and more can be learned from the apple documentation on UITableViewDelegate linked here
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = #"Cell Identifier";
CustomCellClassName *cell = (CustomCellClassName *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil){
cell = [[CustomCellClassName alloc] initWithFrame:CGRectMake(0, 0, tableView.frame.size.width, tableView.frame.size.height)];
//Do basic cell construction common to all cells of this type here
//Set background, image etc.
}
//Do specific cell construction here
return cell;
If you're loading data over the network for each cell, you'll see poor performance. Batch your data fetch, then when it's ready tell your tableview to reload itself.
Using Core Data as a temporary backing store, and an NSFetchedResultsController to retrieve the info from Core Data, will save you some work.

Add row to tableview to add row and core data

I've created a navigation controller based application that uses core data. Without modifying much of the code from the starting application first, I'd like to be able to add rows by having the option to add rows via a dynamic row after I push edit.
Other examples I've found such as the one found at this site show the desired functionality however it does not use core data, so I haven't been able to translate this correctly using core data.
I've looked at the sample application iPhoneCoreDataRecipes and that application includes the desired functionality, however the sample is incredibly complex. Based on the sample app, i've added the following to my - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath function
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// For the Ingredients section, if necessary create a new cell and configure it with an additional label for the amount. Give the cell a different identifier from that used for cells in other sections so that it can be dequeued separately.
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:0];
NSInteger rows = [sectionInfo numberOfObjects];
NSUInteger ingredientCount = rows;
NSInteger row = indexPath.row;
if (indexPath.row < ingredientCount) {
// If the row is within the range of the number of ingredients for the current recipe, then configure the cell to show the ingredient name and amount.
static NSString *IngredientsCellIdentifier = #"IngredientsCell";
cell = [tableView dequeueReusableCellWithIdentifier:IngredientsCellIdentifier];
if (cell == nil) {
// Create a cell to display an ingredient.
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:IngredientsCellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryNone;
}
//
[self configureCell:cell atIndexPath:indexPath];
} else {
// If the row is outside the range, it's the row that was added to allow insertion (see tableView:numberOfRowsInSection:) so give it an appropriate label.
NSLog(#"---- IN ADD INGREDIENTS SECTION ----");
static NSString *AddIngredientCellIdentifier = #"AddIngredientCell";
cell = [tableView dequeueReusableCellWithIdentifier:AddIngredientCellIdentifier];
if (cell == nil) {
// Create a cell to display "Add Ingredient".
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:AddIngredientCellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
cell.textLabel.text = #"Add Ingredient";
}
return cell;
}
When I click the edit button I can delete rows, however I'm not getting the added row to click to add rows. The sample app is much to complex to tell what i'm missing. Is there a function to add to automatically add the 'add row' button the end of the table?
EDIT: Added all of my .M file for reference # http://pastebin.com/Ld7kVts7
When I run my NSLog's 1-12 show in the console. I'm not currently trying to add the "add row" row to core data because that row is added or removed every time the user pushes the edit button in the navigation bar.
Have you changed your the numberOfRowsInSection method of your datasource to account for the extra row that you need?
What you need to do is add the new object to your database, then, assuming your tableView's dataSource is an array form your database, call reloadData on the tableView.

Resources