I would like to update the the text of a label in tableview without reloaddata.
I try ((uilabel *)[self.tableview viewWtihTag:10]).text = Newstring
but its not work.
Is it correct? or have another solution?
Using this method - (void)reloadRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation is correct. However, you should keep track of the value to be loaded in every row in each cell, and then access them every time a row is reloaded when the user scrolls the table.
Here is a sample:
#interface ViewController ()<UITableViewDelegate, UITableViewDataSource>
#property NSMutableArray *labelValueListForSection0;
#property NSMutableArray *labelValueListForSection1;
#property NSMutableArray *labelValueListForSection2;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
_labelValueListForSection2 = [[NSMutableArray alloc] initWithObjects:#"value1", #"value2", #"value3", nil];
}
- (void)changeAnItem
{
[_labelValueListForSection2 setObject:#"ChangedValue" atIndexedSubscript:1];
[_table reloadRowsAtIndexPaths:#[[NSIndexPath indexPathForRow:1 inSection:2]] withRowAnimation:UITableViewRowAnimationNone];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = #"SimpleTableItem";
UITableViewCell *tableCell = [tableView dequeueReusableCellWithIdentifier:identifier];
if(tableCell == nil)
{
tableCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
}
switch(indexPath.section)
{
case 0:
tableCell.textLabel.text = #"Test";
break;
case 1:
tableCell.textLabel.text = #"Test";
break;
case 2:
tableCell.textLabel.text = [_labelValueListForSection2 objectAtIndex:indexPath.row];
break;
case 3:
tableCell.textLabel.text = #"Test";
break;
case 4:
tableCell.textLabel.text = #"Test";
break;
default:
break;
}
return tableCell;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 5;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 3;
}
#end
As you can see, I am keeping track of the value of each row and I am storing them inside an array. In my case, I have an array of values for each row for a specific section. So that when this method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
is called, it will fetch the value that should be assigned to that row.
In case you don't know, iOS is reusing each table cell for better performance and better memory management. So, if you encounter cases such as why is a value of one of my rows is duplicated in other rows, it is because that instance of tableCell is reused in other rows.
So, in order to make sure that each time a cell is loaded, and the value should be correct. You have to keep track of the value for each row and reassign it to that cell each time it is reloaded.
Hope this will help you solve your problem.
Try to reload particular cell of table ...
NSIndexPath* rowToReload = [NSIndexPath indexPathForRow:3 inSection:0];
[myUITableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:rowToReload, nil] withRowAnimation:UITableViewRowAnimationNone];
And add code to change text in cellForRowAtIndexPath:
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
Use this method. This helps you to solve your problem
Related
I am under huge impression of the new SDK I'm implementing and I just want to ask you... how did they do it?
NSInteger rowToDisplayInRead = 1;
NSIndexPath *pathForTeadsInRead = [NSIndexPath indexPathForRow:rowToDisplayInRead inSection:0];
NSString *pid = KTeadsAdPlacementId;
self.teadsAd = [[TeadsAd alloc] initInReadWithPlacementId:pid insertionIndexPath:pathForTeadsInRead repeatMode:NO tableView:self.tableView delegate:self];
Using this code, a new cell appears in the tableview. (Nothing left was set except of the "[self.teadsAd load]" function in viewdidappear().
I didn't even increased number of rows in numberOfRowsInSection delegate).
How to do the same thing?
I couldn't find the terms connected to this operation, but it's great!
Can you help me where to find it?
One of the solution would be to replace the dataSource in the tableView to one that returns the ad cell for chosen indexPath and proxies the "old" datasource for rest of the cells.
So dummy implementation of this would look like this:
#interface Example : NSObject<UITableViewDataSource>
- (instancetype)initWithTableView:(UITableView *)tableView indexPath:(NSIndexPath *)indexPath;
#property (nonatomic, strong) id<UITableViewDataSource> originalDataSource;
#property (nonatomic, copy) NSIndexPath *insertionIndexPath;
#end
m file:
- (instancetype)initWithTableView:(UITableView *)tableView indexPath:(NSIndexPath *)indexPath
{
self = [super init];
if (self) {
self.insertionIndexPath = indexPath;
self.originalDataSource = tableView.dataSource;
tableView.dataSource = self;
}
return self;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSInteger numberOfRows = [self.originalDataSource tableView:tableView numberOfRowsInSection:section];
// the section that will contain injected cell
if(self.insertionIndexPath.section == section) {
return numberOfRows + 1;
}
// any other section
return numberOfRows;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if([indexPath isEqual:self.insertionIndexPath]){
//return cell with an ad
}
if(indexPath.section == self.insertionIndexPath.section && indexPath.row > self.insertionIndexPath.row) {
NSIndexPath *originalIndexPath = [NSIndexPath indexPathForRow:indexPath.row - 1 inSection:indexPath.section];
return [self.originalDataSource tableView:tableView cellForRowAtIndexPath:originalIndexPath];
}
return [self.originalDataSource tableView:tableView cellForRowAtIndexPath:indexPath];
}
You would also need to do the same for rest of the datasource and delegate methods and proxy them with updated index path to original datasource/delegate.
Here i need to implement add objects into array for multiple row selections,here is my code
BOOL selectedRow
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath{
if(selectedRow){
UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
cellText = selectedCell.textLabel.text;
NSLog(#"Airline cellTextC = %#",cellText);
if ([cellText isEqualToString:NameString]) {
NSLog(#"Selected NameString Index = %#",indexes);
[nameArray addObject:indexes];
}else{
NSLog(#"Unknown Selected Name");
}
}
NSLog(#"Total Names = %#",nameArray.description);
}
the above code one section and multiple rows is there ,if am selected a row name string should be add to the Array .It's working fine for only while am selecting one row.But i need add objects from multiple row selections.Can you please suggest me thanks .
You probably want to update the data source of the Table View, and let the Table View Data source methods to update the Table View for you.
Check if this can solve your problem:
[tableView setAllowsMultipleSelection:YES];
if you want to select Multiple rows and add objects to your array i suggest to create 2 NSMutableArray the first one contains all data and the other the selected rows data.
#property (strong , nonatomic) NSMutableArray *myalldatatab;
#property (strong , nonatomic) NSMutableArray *selecteddata;
then use didselectrowsatindexpath and didDeselectRowAtIndexPath to change the mark
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryCheckmark;
}
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryNone;
}
and you can get indexes of selected rows by calling this line :
NSArray *selectedCells = [self.tableView indexPathsForSelectedRows];
you can now iterate selected cells and add objects like this
for (int i = 0; i < [selectedCells count]; i++)
{
[self.selecteddata addObject: [selectedCells objectAtIndex:i]]
}
Hope it help you :)) happy c0de !!
I can't find a simple, concise answer anywhere and I refuse to believe that XCode makes things as hard as other tutorials I've found out there...
Say I have the following array
NSArray* days = [NSArray arrayWithObjects:#"Sunday",#"Monday",#Tuesday",#"Wednesday",#"Thursday",#"Friday",#"Saturday",nil];
I have a UI Table View, table_Days, that I would like to simply show the items from my array. What is the proper way to go about populating my table?
Here's my full explanation, starting with a case extremely similar to yours:
http://www.apeth.com/iOSBook/ch21.html#_table_view_data
So suppose days is stored as an instance variable accessed through a property self.days. Then set self as the table view's datasource and use this code:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
if (!self.days) // data not ready?
return 0;
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
return [self.days count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:#"Cell"
forIndexPath:indexPath];
cell.textLabel.text = (self.days)[indexPath.row];
return cell;
}
You should populate your table view using the data source methods. Returning the count of the array for the number of rows.
If you need to detect when a user taps on a cell you can use the delegate methods.
#interface ViewController<UITableViewDataSource, UITableViewDelegate>
#property (nonatomic, copy) NSArray *days;
#property (nonatomic, strong) UITableView *tableDays;
#end
#implementation ViewController
-(void)viewDidLoad
{
[super viewDidLoad];
UITableView *tableDays; // Set this up
[tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Cell"];
tableDays.delegate = self;
tableDays.dataSource = self;
[self.view addSubview:tableDays];
self.tableDays = tableDays;
self.days = #[#"Sunday", #"Monday", #"Tuesday", #"Wednesday", #"Thursday", #"Friday", #"Saturday"];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.days count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
cell.textLabel.text = self.days[indexPath.row];
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *day = self.days[indexPath.row];
NSLog(#"Day tapped: %#", day);
}
#end
You should consider using a UITableViewController if you just want to show a table view.
Note that its better practice to use camel case for variables.
Note: when I tap the row, then the app crashes.
I'm trying to implement adding a new cell on a user's tap. I found that there was a similar example in WWDC 2011's table view demonstration. Here's my code from my table view.
Here is the error:
2013-03-19 20:04:28.672 Project[51229:c07] *** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-2380.17/UITableView.m:1070
Here is my code from the table view.
#interface MyPFQueryTableViewController : PFQueryTableViewController <PFLogInViewControllerDelegate, PFSignUpViewControllerDelegate>
#property (nonatomic, strong) NSIndexPath *controlRowIndexPath;
#property (nonatomic, strong) NSIndexPath *tappedIndexPath;
#implementation MyPFQueryTableViewController {
ListItemObject *listDetail;
}
#synthesize controlRowIndexPath;
#synthesize tappedIndexPath;
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
PFObject *object = [self.objects objectAtIndex:indexPath.row];
[object deleteInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
[self loadObjects];
}];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object {
static NSString *CellIdentifier = #"listCell";
PFTableViewCell *cell = (PFTableViewCell *)[tableView dequeueReusableCellWithIdentifier:#"listCell"];
if (cell == nil) {
cell = [[PFTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell
cell.textLabel.text = [object objectForKey:self.textKey];
//cell.imageView.file = [object objectForKey:self.imageKey];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
//if user tapped the same row twice let's start getting rid of the control cell
if([indexPath isEqual:self.tappedIndexPath]){
[tableView deselectRowAtIndexPath:indexPath animated:NO];
}
//update the indexpath if needed... I explain this below
indexPath = [self modelIndexPathforIndexPath:indexPath];
//pointer to delete the control cell
NSIndexPath *indexPathToDelete = self.controlRowIndexPath;
//if in fact I tapped the same row twice lets clear our tapping trackers
if([indexPath isEqual:self.tappedIndexPath]){
self.tappedIndexPath = nil;
self.controlRowIndexPath = nil;
}
//otherwise let's update them appropriately
else{
self.tappedIndexPath = indexPath; //the row the user just tapped.
//Now I set the location of where I need to add the dummy cell
self.controlRowIndexPath = [NSIndexPath indexPathForRow:indexPath.row + 1 inSection:indexPath.section];
}
//all logic is done, lets start updating the table
[tableView beginUpdates];
//lets delete the control cell, either the user tapped the same row twice or tapped another row
if(indexPathToDelete){
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPathToDelete]
withRowAnimation:UITableViewRowAnimationNone];
}
//lets add the new control cell in the right place
if(self.controlRowIndexPath){
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:self.controlRowIndexPath]
withRowAnimation:UITableViewRowAnimationNone];
}
//and we are done...
[tableView endUpdates];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if([indexPath isEqual:self.controlRowIndexPath]){
return 45; //height for control cell
}
return 70; //height for every other cell
}
- (NSIndexPath *)modelIndexPathforIndexPath:(NSIndexPath *)indexPath
{
int whereIsTheControlRow = self.controlRowIndexPath.row;
if(self.controlRowIndexPath != nil && indexPath.row > whereIsTheControlRow)
return [NSIndexPath indexPathForRow:indexPath.row - 1 inSection:0];
return indexPath;
}
#end
The problem is in your didSelectRowAtIndexPath method. You have:
[tableView beginUpdates];
//lets delete the control cell, either the user tapped the same row twice or tapped another row
if(indexPathToDelete){
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPathToDelete]
withRowAnimation:UITableViewRowAnimationNone];
}
//lets add the new control cell in the right place
if(self.controlRowIndexPath){
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:self.controlRowIndexPath]
withRowAnimation:UITableViewRowAnimationNone];
}
//and we are done...
[tableView endUpdates];
Before you make any calls to tell the table to add or remove any rows, you must update your data source with by adding or removing data. The table will check how many sections and rows there are before and after your add/remove rows. The number of sections and rows after the change must properly reflect how much data you add/remove with how many rows you add/remove.
And of course you must implement the numberOfRowsInSection method.
What does your - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section look like?
This error happens when you try to add or delete a row from the UITableView, but the number of rows that you claim to be in the section after the update in that method is not consistent with the new data that should be loaded.
Ex, if your numberOfRowsInSection always returns 4 and you add a row in that section, the tableView will want it to be 5, but it will not be so it will crash. You need to keep track of how many rows are in each section and return that number.
I'm building a app which has a table view. But I want this to be a table view which expands the cell when you tap on it and close when you tap a second time.
But I was wondering if the following is possible. When the cell isn't selected you only see a picture, title and the beginning of the text. But as soon as the cell is selected, it will expand and show even more subviews, i.e. image views.
Is this possible? For instance, to hide a subview in the cell, and as soon a it is tapped it's visible and aligned the right way? And of course, how do i do that?
Thnx!!!
I did something similar quite a few time ago. You'll find the code at github.
Note that it is very rough, as it where my beginning iPhone days, i.e. properties are missing.
.h
#import <UIKit/UIKit.h>
#interface FirstViewController : UITableViewController <UITableViewDelegate, UITableViewDataSource> {
NSIndexPath *selectedIndexPath;
NSDictionary *articles;
}
#end
.m
#import "FirstViewController.h"
#implementation FirstViewController
- (void)viewDidLoad {
[super viewDidLoad];
selectedIndexPath = nil;
articles = [[NSDictionary dictionaryWithObject:[NSArray arrayWithObjects:#"one", #"two", #"three",
#"four", #"five", #"six",
#"seven", #"eight", #"nine",
#"ten", #"eleven", nil]
forKey:#"title"] retain];
}
- (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;
}
- (void)dealloc {
[selectedIndexPath release];
[articles release];
[super dealloc];
}
- (int)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[articles allKeys] count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return [[articles allKeys] objectAtIndex : section];
}
- (int)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section
{
id key = [[articles allKeys] objectAtIndex:section];
return [[articles objectForKey : key] count];
}
- (float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if ((selectedIndexPath != nil) && (selectedIndexPath.row == indexPath.row))
return 80.0;
return 40.0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString * MyIdentifier = #"MyIdentifier";
UITableViewCell * cell = [self.tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] autorelease];
}
id key = [[articles allKeys] objectAtIndex:indexPath.section];
cell.textLabel.text = [[articles objectForKey:key] objectAtIndex:indexPath.row];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (selectedIndexPath == indexPath) {
selectedIndexPath = nil;
} else {
selectedIndexPath = indexPath;
}
[self.tableView deselectRowAtIndexPath : indexPath animated : NO];
[tableView beginUpdates];
[tableView endUpdates];
}
#end
Yes, I do this in the app I'm working on now.
You need to keep track of the state that a cell is in, either open or closed. If only 1 cell can be open at a time, you can do this by just keeping a reference to the current indexPath. If multiple cells can be open at the same time, you'll need an array of booleans, which tracks if each one is open or closed.
In heightForRowAtIndexPath, just return the correct height based on if the row is open or closed.
In cellForRowAtIndexPath, if the row is closed, hide all the content that shouldn't be visible when it's closed. The views can still be there, but they should be set to hidden = YES.
Finally, in didSelectRowAtIndexPath, set the given index path to open if it was closed, and closed if it was open, then reload the cell with [tableView reloadRowsAtIndexPaths:]. If you are only allowing 1 at a time to be open, then just set your current open index path to the one that was selected, and reload both the one that was selected as well as the one that had been previously open.