I'm adding subviews with UITableViewCell default type. In this, assume that, I've only 2 rows. At first, when it comes to tableView:cellForRowAtIndexPath: I'll generate a dynamic identifier for each cell based on indexPath.row. So it goes well and create a new cell inside if(cell == nil) condition. I'm adding subviews inside this condition only.
Next, out side that condition, I'm simply updating content of subview by fetching them out of the cell's contentView. This works great.
Problem is here : I'm expanding my cell (on which user tapped). When I do this, it'll go into tableView:cellForRowAtIndexPath: and I'm able to see the same identifier generated before. However, its going inside if(cell == nil) condition.
For a note, I'm checking for that condition like this,
if(!cell) and not if(cell == nil).
Can you guess what is the issue? However in my deep investigation, I found that, it'll only goes inside if(cell == nil) once I expand particular row and not next time for the same row.
Here's the sample :
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
//number of count
return [array count];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
//dynamic height will be provided based on content.
return [self heightForRow:indexPath];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *cellIdentifier = [NSString stringWithFormat:#"cellIdentifier_%ld", (long)indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
[self setupCell:cell forRow:[indexPath row] inTable:tableView];
}
[self updateCell:cell forRow:[indexPath row] inTable:tableView];
return cell;
}
- (void) setupCell:(UITableViewCell *)cell forRow:(NSInteger)row inTable:(UITableView *)tableView {
//setting up view here
}
- (void) updateCell:(UITableViewCell *)cell forRow:(NSInteger)row inTable:(UITableView *)tableView {
//updating view here
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
self.expandedIndexPath = ([indexPath compare:self.expandedIndexPath] == NSOrderedSame) ? nil : indexPath;
[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView endUpdates];
[tableView scrollToRowAtIndexPath:self.expandedIndexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
}
Hope my issue is clear to you. Please comment if you don't get something or found anything missing here (and I'll update it here) or answer if you know what's the solution to this.
Thanks!
Related
I have the following code to create my cells in a UITableView.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 5;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = #"futureAppointments";
FutureAppointmentsViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (nil == cell) {
[tableView registerNib:[UINib nibWithNibName:#"FutureAppointmentsViewCell" bundle:nil] forCellReuseIdentifier:CellIdentifier];
cell = [[FutureAppointmentsViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
NSUInteger position = indexPath.row;
cell.appointmentDescription.text = [NSString stringWithFormat:#"%lu. %#. %#", (unsigned long)position, #"Steve", #"10/03/"];
return cell;
}
The problems is, the first cell of the tableView is missing. It should start with 0. Steve but instead starts with 1. Steve. Also there are only 4 elements in the list instead of 5.
When I place a break point in the code, the first cell is nil.
Does anyone know what might be happening?
Put this line of code:
[tableView registerNib:[UINib nibWithNibName:#"FutureAppointmentsViewCell"
bundle:nil]
forCellReuseIdentifier:CellIdentifier];
In viewDidLoad. It doesn't belong in cellForRowAtIndexPath. Once you do this then you no longer need to check whether the cell is nil.
Change cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = #"futureAppointments";
FutureAppointmentsViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
NSUInteger position = indexPath.row;
cell.appointmentDescription.text =
[NSString stringWithFormat:#"%lu. %#. %#",
(unsigned long)position, #"Steve", #"10/03/"];
return cell;
}
I think I had similar problem when I was using UITableView inside UIView and initialized it in init method.
I was not able to find good explanation for that behavior, but I've found tricky solution for that - I was reloading UITableView instance from UIViewController in viewDidAppear method.
I would also like to know, why UITableView is not drawing all UITableViewCell.
In a VC I have 2 tableViews - 1 main and other is added in a drop down menu. From drop down menu TableView "ddTableView", I have added 5 cells in Storyboard itself as prototype cells. Each cell contains an image and a Label. Have also set identifier for each cell. Have also set Accessory Type as "Disclosure Indicator" for each cell of ddTableView.
DataSource and Delegate for mainTV is set to the VC and delegate for ddTableView is set the VC. As the rows are added within the storyboard, I didn't set any datasource for ddTableView in my VC. I managed my delegate methods like this :-
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView == ddTableView) {
// RETURNS NIL ???
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
NSLog(#"CELL Identifier = %#", cell.reuseIdentifier);
return cell;
} else {
}
}
-(void )tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView == ddTableView) {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
NSLog (#" CELL Clicked of - %#", cell.reuseIdentifier);
}
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView == ddTableView)
return 44;
else
return 60;
}
-(NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == ddTableView)
return 5;
else
return [chatMsgsArr count];
}
It does call respective delegate method. For cellForRowAtIndexPath it returns cell as nil. Why so ? And on execution, I don't see any contents of the table view - table is blank. I changed the bg color of image and text color also, but yet nothing is seen on execution.
Why so ? Where I may be going wrong ?
You have to allocate the cell, the first time cellForRowAtIndexPath is called. If the cell is not allocated it would return nil as you are getting right now.
What you need to do is allocate the cell only once, the first time you enter cellForRowAtIndexPath. After that you can reuse it as you have done.
If you are planning to use the default tableViewCell your method should look as follows:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellIdentifier = #"your_cell_identifier_name";
if (cell == nil)
{
UITableViewCell *cell = [UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
else
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
}
//write the code to populate your cell with data here
//return the cell object
return cell;
}
Hope this helps! :)
- (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] ;
}
if (tableView == ddTableView) {
NSLog(#"CELL Identifier = %d", indexpath.row);
} else {
NSLog(#"CELL Identifier = %#", [chatMsgsArr objectAtIndex:indexPath.row]);
}
return cell;
}
I have one UIViewController with UITableView inside,above table I have UISegmentControl, when I press on segment control I want to load a UItableCustomeCell, would you please help me in this implementation, I don't know how should I add them in cellForRowAtIndexPath, Since I have 3 different Custom cell
Here is the code
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath
*)indexPath
{
if (indexPath.row == self.segmentedControl.selectedSegmentIndex == Test1) {
MytestsCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MytestsCell"];
if (!cell) {
cell = [[MyBooksCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"MytestsCell"];
}
return cell;
}
else if (indexPath.row == self.segmentedControl.selectedSegmentIndex == tests) {
testCell *cell = [tableView dequeueReusableCellWithIdentifier:#"testCell"];
if (!cell) {
cell = [[TestsCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"testsCell"];
}
return cell;
}
break;
case 1:
if (indexPath.row == self.segmentedControl.selectedSegmentIndex == PTest) {
PTestsCell *cell = [tableView dequeueReusableCellWithIdentifier:#"PTestsCell"];
if (!cell) {
cell = [[PTestsCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"PTestsCell"];
}
return cell;
}
break;
}
I don't want to have 3 of them in one table, each custom cell is for one segment control
Thanks in advance!
One alternative I can think of is to switch the table views data source. But I would not recommend that. You could define a delegate of your data source and ask it for the table view cell for a selected segmented control. But this just moves the problem. I would stick to your approach.
So...here is what I would do. Starting with iOS6, you no longer need to check if your cell is nil after dequeuing from the tableview if you use
- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath
You are guaranteed to get a cell back as long as the identifier exists. Also, it doesn't look like you need to do any additional configuration so something like this should work:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *identifier = [NSString stringWithFormat:#"%d", self.segmentedControl.selectedSegmentIndex];
return [tableView dequeueReusableCellWithIdentifier:identifier forIndexPath:indexPath];
}
Edit: I forgot to add, that in order to use this, use numbers that correspond to the segments as the identifier for each cell.
I have UITableView on iPad. When a cell is selected and the cell is scrolled off the screen, and then scrolled back into the screen, the selected cell is blank.
My problem occurs only on the device and not on simulator. I have tried various of the proposed solutions like manage selected index
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.selectedIndexPath = indexPath;
}
and use setSelected/selectRowAtIndexPath on cellForRowAtIndexPath like
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Desktop Row";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell =[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
...
if(self.selectedIndexPath!=nil){
if ((indexPath.row == self.selectedIndexPath.row) && (indexPath.section == self.selectedIndexPath.section)) {
[self.tableView selectRowAtIndexPath:self.selectedIndexPath
animated:YES
scrollPosition:UITableViewScrollPositionNone];
}
}
return cell;
}
But nothing works, I saw very similar questions people ask but I have something different and strage.
Any ideas will be appreciated.
I want to have the same behavior as Twitter app when selecting a tweet that is : expanding the row and adding supplementary content.
So this is not only a basic row resize. I think i need 2 different custom cells, so i did something like that :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([indexPath compare:self.selectedIndexPath] != NSOrderedSame) {
FirstCell *cell = [tableView dequeueReusableCellWithIdentifier:#"FirstCell"];
if (!cell) {
cell = [[FirstCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"FirstCell"];
}
cell.firstnameLabel.text = [[self.items objectAtIndex:indexPath.row] objectForKey:#"firstname"];
return cell;
}
else {
SecondCell *cell = [tableView dequeueReusableCellWithIdentifier:#"SecondCell"];
if (!cell) {
cell = [[SecondCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"SecondCell"];
}
cell.firstnameLabel.text = [[self.items objectAtIndex:indexPath.row] objectForKey:#"firstname"];
cell.lastnameLabel.text = [[self.items objectAtIndex:indexPath.row] objectForKey:#"lastname"];
return cell;
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if ([indexPath compare:self.selectedIndexPath] == NSOrderedSame) {
return 80.0;
} else {
return 44.0;
}
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.selectedIndexPath = indexPath;
[tableView reloadData];
}
My problem here is that i want to keep a fluid animation like Twitter app which is not my case because of [tableView reloadData] (which is mandatory i think since i have 2 different custom cell).
So anyone knows a workaround to my needed or maybe someone knows how Twitter app is handling this animation stuff ?
You want to reload that cell with an animation:
[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];
[tableView beginUpdates];
[tableView endUpdates];
this triggers an update of the cell row sizes for the whole tableView.... referenced from :iPhone - Smooth animation for UITableViewCell height change including content update
Happy Coding!
It is actually rather easy to implement this using two custom cells like you originally intended by just combining your original code with rooster117's solution. Replace [tableView reloadData] in your tableView:didSelectRowAtIndexPath: with:
[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView endUpdates];
and you should have your solution.