UITableView inside a UITableViewCell getting hide when scrolls the outer tableview - ios

I'm developing an app where the user can add events to a particular contact in contact list. So, if the user scrolls the contact list, each contact will show the list of events in it. My problem is when I scroll down the outer table view, the events are showing up fine, but when scrolling up it hides some of the events(i.e., UITableViewCells). And again if I start scrolls down its working fine and again scrolls up the events are hiding. For eg., If a contact has three events in it, it shows only first two and last one is hiding. Checked will the datasource methods, all are fine(i.e., the CellForRowAtIndexPath getting called 4 times).
My CellForRowAtIndexPath of inner tableView:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"EventViewCell";
EventsCellAtViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
// load a new cell from the nib file
[[NSBundle mainBundle] loadNibNamed:#"EventsCellAtViewCell" owner:self options:nil];
cell = self.eventsAtCell;
self.eventsAtCell = nil;
}
cell.selectionStyle = UITableViewCellSeparatorStyleNone;
cell.event = [_eventsFeed eventAtIndex:indexPath.row];
return cell;
}
Please help me out in this issue. Thanks in Advance!!

Putting a vertical scroll view inside another vertical scroll view is often a cause of problems. The user often gets too confused about which view should have scrolled when they dragged up or down.
In this case it sounds like the data inside the cell is taller than the cell itself, causing it to need to scroll to display it all.
Instead of trying to scroll the data inside a cell, you should make the cell tall enough to display all its data. The heightForRowAtIndexpath method will give you the chance to change each cell's height.
It also looks like this is based on older Apple sample code. The UINib method of instantiating cells is preferable to using "loadNibNamed" to reset the IBOutlet with a new cell. Using storyboards is more modern than both of these methods.

Related

Why table view is getting slow after implementing rating stars in iOS?

I am trying to implement rating stars by using kDRATING VIEW .i have used following code in cellForRowAtIndexpath method but it causes my app to become slower.
If it try to allocate and initialise this in viewdidload method then it when i scroll up and down ,the stars fluctuates . please help in this regard
self.rating = [[KDRatingView alloc] initWithFrame:CGRectMake(0, 0, 60,20)];
[self.rating rateKDRatingView:2.80 outOf:3.0];
[cell.rating addSubview:self.rating ];
return cell;
It sounds like you need to look into UITableViewCell reuse because when you scroll a UITableViewCell out of the screen it will call cellForRowAtIndexPath again to remake this cell when it is back in view and that can cause flickering and memory consumption.
You are adding the KDRatingView to the rating view on the cell so I guess you have a custom cell, so why not instead have the KDRatingView inside the custom cell and just update its value when you need to.
Try this solution with some cell reuse:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"RatingCell";
RatingCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil){
// initialisation code
cell = [RatingCell new];
}
// setting code
[cell setRatingViewValue:2.8 outOf:3.0];
}
That way it only creates the cell once, so it won't slow down your app. Then each time the cell would have been recreated it just updates the cells rating (and anything else you need to set) instead.
Then you just need to implement the setRatingViewValue:outOf: method in your custom cell to update the KDRatingView that you need to have added and positioned in your custom cell upon initialisation.

Using dequeueReusableCellWithIdentifier does not appear faster (or lower memory)?

When I want to create a table with custom cells, this is how I will write the standard codes:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"CustomCell";
CustomCell *cell = (CustomCell*)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
{
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
cell = [topLevelObjects objectAtIndex:0];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
else
{
NSLog(#"reuse!");
}
cell.property1 = ....
cell.property2 = ....
return cell;
}
This is roughly how I will create the custom cells:
create a CustomCell.h (inheriting UITableViewCell) and CustomCell.m and a CustomCell.xib
in the CustomCell.xib, change the 'class' property of the top level UIView to 'CustomClass'
I have been doing this for sometime but today I finally decided to do a test to see if the table is really reusing the cells correctly.
Nope. The table is NOT reusing any cell!. The NSLog(#"reuse!") in the code snippet above is never triggered.
I decided to do a performance comparisons, with a tableview with 1 million cells, using these two methods:
Method #1 used the method described above. Take note that UIView is the top level view in my CustomCell.xib and no reuse identifier is configured on xib
cell not reused (NSLog reused! not printed)
peak memory about 3.4MB, peak CPU 60%
scrolling is smooth
Method #2 uses a UITableViewCell in the top level of the xib, and I put all my controls on the content view of this UITableViewCell. In the XIB, I configured the reuse identifier of this cell to be 'CustomCell'.
cell reused (NSLog reused printed multiple times)
peak memory about 6.1MB, peak CPU about 88%
scrolling is smooth
Two questions:
Why am i not seeing much lower CPU and memory usage on method #2, isn't method #1 wrong and method #2 right because method #1 is not reusing any cell at all? Or looking from another point of view, why method #1 is still scrolling so well even though it is stupidly loading nib again and again?
When creating a custom xib for a custom cell, does it make any difference whether the top level object is a UIViewnor UITableViewCell? (Looks like no difference?)
It seems that reuseIdentifier is not set properly in your XIB file.
You can set it programmatically also by adding
[yourTable registerNib:[UINib nibWithNibName:#"CustomCell" bundle:nil] forCellReuseIdentifier:#"CustomCell"];
somewhere before using table view. In viewDidLoad for example.
UPDATE
Question 1. If you do not set cell reuse identifier cells life circle is following:
1.It is created.
2.It is shown.
3.It is moved out of visible area.
4.It is not needed anymore so it is deleted.
If you use reuse identifier cells TYPICAL life circle is:
1.It is popped from reusable queue..
2.It is shown.
3.It is moved out of visible area.
4.It is pushed to reusable queue.
So performance difference is due to what is executed faster: create/release operation or pop/push. Memory usage should be approximately the same for both variants.
you need implement
-(NSString *)reuseIdentifier {
return #"CustomCell";
}
in you cell class CustomCell, because reuseIdentifier is readonly property you can't set it from outside

Subviews of UITableViewCell seem to randomly disappear after dequeueReusableCellWithIdentifier

In my iOS 7.0 App:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
...
// AttemptCell is a prototype cell, currently using the "Right Detail" preset
// style and the little information accessory.
static NSString *CellIdentifier = #"AttemptCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
assert(cell != Nil);
if (cell.contentView.subviews.count == 2)
{
UILabel *attemptLabel = (UILabel*)cell.contentView.subviews[0];
attemptLabel.text = attempt.attempt;
UILabel *analysisLabel = (UILabel*)cell.contentView.subviews[1];
analysisLabel.text = [attempt analysis];
cell.tag = indexPath.row;
}
else
{
// Something has gone very wrong.
UILabel *attemptLabel = (UILabel*)cell.contentView.subviews[0];
attemptLabel.text = #"Error";
}
The question is why does the (UILabel*)cell.contentView.subviews[1] sometimes disappear causing the error block to be entered.
This table view shows one custom keyboard entry cell (UITextField) which always appears last. The keyboard entry cell is also prototyped, but with a different dequeue cell identifier. The problem is randomly seen when the keyboard pops up and is closed. Keyboard popping up causes some AttemptCells to go out of view and closing the keyboard causes the AttemptCells to come back into view.
What you are doing is wrong. Don't rely on the view hierarchy of a private class, certainly don't depend on the number of views in a hierarchy and really don't depend on a view being in a certain position of the sub views array. Your error block may not be entered because a sub view has "disappeared" - an extra view could have been added, all you're checking for is that the count of the sub views is equal to 2.
If you're using one of the standard cell layouts, use the textLabel and detailTextLabel properties. If you're using a subclass, use outlets.

Removing old data from reusable cell

I have a uitableview with each cell having a scroll view as the subview.
the scrollview has a bunch of images in it.
so when i change the data in the data source and after calling the reload table
the images doesn't change but when i remove the dequeue the new data is reloaded.
is there any method to remove the contents in the dequeue so that i don't get the old data
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"looser"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
scrollview=[[myscrollView alloc]initwitharray:imagearray];
[cell.contentView addSubview:scrollview];
}
}
A tableview works as follows:
It has room for a certain amount of cells on the screen, let's say 7 as an example. The tableview will ask you for the 7 cells of indexes 0 through 6.
If the top cell leaves the screen by scrolling, it will be placed in the reusable cell queue. There are now 6 cells on the tableview.
A new one comes up at the bottom now, the tableview asks for the cell at index 7. You call dequeueReusableCell, and you get the one that was at the top earlier.
The tableView has no idea what your cell is like, as it can be subclassed, so it will not make any changes to it. It is up to you to use your knowledge of how the tablecell is constructed to empty it, then fill it with the correct new data.
The reason tableview works like this is for performance. In stead of having maybe 100 views that would have to be checked (or mostly, ignored, which also costs time) for every scroll movement, it has a maximum of 7.
So in short, no. There are no default methods to remove data from reusable cells in UITableView, since UITableView can not and should not know what kind of cells they are. It is up to you to clear the cells when the tableview gives them to you.
Create a custom cell and it generates a method
- (void) prepareForReuse{}
Which do you cleanse all data from a cell and the output will be an empty cell.
No, not while the cell is in the cache. When you dequeue a reusable cell you should clear out the old data first before using it again.
Maybe you should just remove the stuff you don't want.

UITableview Scroll erases data in text field inside UITableviewcell

I have a UITableViewController with UITextfield inside the tableview cells. If I scroll the table view, the user entered data in the textfields disappears. I tried to add the textfield data to a NSMutableArray but it still didn't work. Any help please.
When cellForRowAtIndexPath: is called, the cell you return has to be completely filled in with whatever data you want to show. So, if the cell includes a UITextfield, you'll need to set it's text property to the right value for that row in your data.
When a table cell disappears off the top or bottom of the screen, the UITableViewCell itself becomes available for re-use. (As you scroll, cells disappear, and new cells appear, but the UITableView class is re-using the UITableViewCell objects.) In cellForRowAtIndexPath: when you get a cached cell to use, you have to be sure to setup everything you want it to show for the row in question, otherwise you might see some odd behavior in your table.
Does this help?
EDIT:
Here's an example of the typical pattern used in cellForRowAtIndexPath:. Notice the use of dequeueReusableCellWithIdentifier:. That method returns a previously allocated but not in use UITableViewCell, if there is one. Notice further that if no cached cell is returned, the code creates a new one, and sets it up (with stuff that is independent of anything that might be row specific). Following that, you'd setup the cell as you need it for the row in question.
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *SearchResultsCellIdentifier = #"SearchResultsCellIdentifer";
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:SearchResultsCellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:SearchResultsCellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
// Your row-specific setup of the cell here
// ...
return cell;
}
Check the docs for specifics about these methods. There are LOTS of examples from Apple and elsewhere about how to implement tableViews.

Resources