Subviews of UITableViewCell seem to randomly disappear after dequeueReusableCellWithIdentifier - ios

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.

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.

Can't access a UIButton when it's created dynamically in UITable view cell?

I've created a UIButton dynamically in the table view cell. but there is a problem,- I'm not able to access the button or it's sender method.
When I click on a button it's giving the wrong tag or sometime it's not clickable. I've created button a with the help of for() loop in the tableview cell. I think the main problem is that I create the button in the for() loop. Can anyone help me to solve this issue?
My code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = (UITableViewCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
UIView *viewDisplaySize;
if (cell == nil)
{
NSString *myString =#"1111111:2222222:333333:44444:55555:6666:777777:888888888888:9999999999" ;
NSArray *myWords = [myString componentsSeparatedByString:#":"];
for (int i=0; i < [myWords count]; i++) {
//Create a button but can't able to get a correct tag
/// Or some time it's not click able
}
Whole code--- link of full code
The basic problem is that the way you're adding your subviews is not the best way. By adding the subviews inside a if(cell == nil) clause, and setting the tag value there, the tag value will never change. When the cell is reused, the tag will still be based on the row where it was created, not on where that reused cell now appears.
A better way to create your cell, would be to add any subviews you need in the init method of a custom cell class. This cleans up the code in cellForRowAtIndexPath so it only contains code you need to populate the cells. If the cell is made completely in code (no xib or storyboard for its view), then you should register the class (usually done in viewDidLoad of the table view controller) with registerClass:forCellReuseIdentifier:. Then, in cellForRowAtIndexPath there will be no need to check whether the cell is nil because it never will be. You still want to set the button's action and tag in cellForRowAtIndexPath, but now it will be reset for the proper row when the cell is reused.

UITableView inside a UITableViewCell getting hide when scrolls the outer tableview

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.

Creating a reusable UIView similar to UITableViewCell

Ahoy!
I'm trying to create a reusable UIView (for various reasons) similar to the UITableViewCell implementation used in UITableViewController. I'd like to use the reusable view in a UIScrollView so I know i'm not trying to achieve something that's entirely unattainable.
The default implementation of this is:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//declare cell identifier
static NSString *cellIdentifier = #"cell_identifier";
//dequeue cell
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
//check cell is valid
if(cell == nil)
{
//create a new cell
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
}
//
//return cell
return cell;
}
From this, it's worth noting that the cell is dequeued from the UITableView. If the cell is invalid, a new cell is created. My question is, how does this cell then become "queued" for reuse later?
My current attempted implementation looks like this:
- (TestScrollViewCell *)scrollView:(TestScrollView *)_scrollView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//declare cell identifier
static NSString *cellIdentifier = #"cell_identifier";
//dequeue cell
TestScrollViewCell *cell = (TestScrollViewCell *)[scrollView dequeueReusableCellWithIdentifier:cellIdentifier];
//check cell is valid
if(cell == nil)
{
//create a new cell
cell = [[TestScrollViewCell alloc] initWithFrame:CGRectZero];
}
//
//return cell
return cell;
}
I'm thinking that adding a NSMutableDictionary to my TestScrollView to store the cellIdentifier and the TestScrollViewCell (UIView) and then plucking them back out based on the dictionary key would be a good start but is this really a true implementation of "reusable" cells?
The issue I can see is that I would then be adding the UIView to the ScrollView which is positioned based on the frame. Dequeing a view in this sense wouldn't allow me to then add the view to the scroll view without affecting the first view (by modifying the frame) but surely this is how UITableViewCells work, as well as section headers/footers?
I've been looking at this implementation which seems to be following the same route I was intending on implementing but i'm not 100% sold that this is a true implementation of reusable cells.
Has anyone had any luck with this previously? I'm trying to take Apple's lead on this one but other than UITableViewCell and MKAnnotationView (MapKit) there aren't any accessible implementations of this for me to glean from.
Any help would be greatly appreciated.
It's not just the view, it's the whole UITableViewController you'll need to recreate. The reuse flow goes like this:
dequeueReusableCell gets empty reused cell from some storage, I guess, from NSMutableArray (grab first object from array, then delete it from array and return it). If array is empty, method returns nil. You check for cell value, if it's nil, you create a new instance of cell class. If it's not nil, you fill it with your data.
This goes for every visible cell, that is, every cell that can fit on screen. Any non-visible cells are not initialized. When user scrolls the table, cell that are gone completely off-screen (not a single pixel visible) sent to reuseQueue – all their subviews and values return to default values or just nilled, and then cell gets added to the end of our NSMutableArray that is the queue.
I hope I explained well enough.
EDIT: Oh, and one more thing - you'll need different reuse queues for each reuse identifier.

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