Adding dynamic cells to a static UITableView: dynamic cells are empty - ios

I've done my research and followed the numerous guides for this process, including:
Adding unknown number of rows to 'Static Cells' UITableView
https://devforums.apple.com/message/502990#502990
But a recurring theme in the follow-up questions is always "The TableViewCells show up, but they are empty." I assume people solve the problem, but no solutions are posted.
Thus, the stage I am at consists of: the static cells showing up and being correctly filled with data, and the dynamic cells correctly show up in quantity, but not with their elements (they are empty).
I believe I have everything hooked up correctly. I have:
In my UITableViewController subclass, included and overridden all required methods as marked as "Answer" in the two links above.
Subclassed UITableViewCell, and included two UILabel properties in the subclass.
Set the class for the cell in Storyboard to my subclass, and given an appropriate Identifier, which is correctly used in the Controller subclass.
Placed two UILabels on the cell in storyboard.
Hooked up the two labels to the properties in the Cell subclass.
I instantiate and assign values to the properties just like in the answers above.
static NSString *CellIdentifier = #"DynamicCell";
OwnersInfoEventsCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
cell = [[OwnersInfoEventsCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.eventName.text = #"Name"; //this doesn't show up
cell.eventNeed.text = #"Need"; //this doesn't show up
cell.backgroundColor = [UIColor redColor]; //this works, the cell shows up red
return cell;
What am I missing?

That happened to me and the problem was that when you create your own cell (in case you don't find a reusable one) its two labels are nil. So you also has to create two labels and set them as the labels of your cell and it would hopefully work.

First you need to check that you have defined OwnersInfoEventsCell properly. Check that Label that you defined in Cell Class is properly defined. Check frame of that label.
_ eventName = [[UILabel alloc]initWithFrame:CGRectMake(25, 2, 40 , 40)];
_ eventName.backgroundColor = [UIColor clearColor];
[_ eventName setBaselineAdjustment:UIBaselineAdjustmentAlignCenters];
eventName.font = [UIFont fontWithName:#"King" size:12];
[self.contentView addSubview:_ eventName ];
check that you have added label in cell's content view. This may be issue. let me show your code of OwnersInfoEventsCell then i can suggest you more briefly.
Hope this may help you.

Related

Create a UISwitch in one tableview cell - UISwitch gets duplicated

I have 11 or more number of rows. Need to create a UISwitch only in the first cell. The UIswitch gets duplicated when i click on any row.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:EN_MoreTableViewCell];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:EN_MoreTableViewCell];
cell.backgroundColor = [UIColor clearColor];
}
cell.textLabel.text = languageObject.name;
[cell.textLabel setFont:font];
if (indexPath.row == 0 && [languageObject.name isEqual: #"All Languages"]) {
if (!mySwitch) {
mySwitch = [[UISwitch alloc] initWithFrame:CGRectMake(_languageListTableView.frame.size.width - 60, 0, 40, 40)];
[cell.contentView addSubview:mySwitch];
}
cell.accessoryType = UITableViewCellAccessoryNone;
}
else { //other cell code//
}
Please help.
This is a classic iOS newbie question. This confuses the hell out of most of us when we first use table views (it certainly confused me at first.)
Cells get created once and then reused over and over. The function dequeueReusableCellWithIdentifier() returns a recycled cell if one is available, or creates a new one from your cell prototype if not.
If you get a recycled cell, it will already have your switch added.
The cleanest way to handle this is to define a cell prototype using a custom subclass of UITableViewCell that has all your fields already added and connected as outlets to the cell. Then when you dequeue a cell, simply cast it to your custom UITableViewCell class and use the properties you've defined to access your custom fields (Your switch, in this case.)
A recycled cell may also contain values in it's other fields (Like if you've set a label field to contain a string, it will still contain the string.) You need to clear out old values and completely configure a recycled cell. (The custom cell class and prototype doesn't fix this problem. You always need to fully configure every field in your cell.)
Either:
Create two prototype cells in Storyboard, one with and one without UISwitch and dequeue the first only if indexPath.row == 0.
Or:
Add the UISwitchto your cell in Storyboard, make an IBOutlet to your cell and set self.mySwitch.isHidden = true in cells prepareForReuse().
This way the default state when reusing the cell is with hidden switch.
Later if indexPath.row == 0, set cell.mySwitch.isHidden = false.
It's because cells are reused. You can either remove all existing subviews in -[UITableViewDataSource tableView:cellForRowAtIndexPath:] or create a new cell for every row.
Sometimes a custom cell is a big hammer for just adding a single view to an otherwise perfectly good standard UITableViewCell. For those occasions, lazy creation is a nice pattern to get views built exactly once on reused cells (or even as any subview of any view). It works like this:
// in your cellForRowAtIndexPath, after dequeuing cell
UISwitch *switch = (UISwitch *)[cell viewWithTag:64]; // make up a unique tag
if (!switch) {
switch = [[UISwitch alloc] initWithFrame:...];
switch.tag = 64; // 64 must match the tag above
[cell addSubview:switch];
}
// here, switch is always valid, but only created when it was absent
Add a UISwitch in the storyboard. Connect outlet. In tableview's cellforrowatindexpath, if the index is 0 set hidden=false else set hidden=true. Hope this works.
You just hide the switch where you don't want to display and set the action for that switch dynamically for the particular indexpath you want

I am adding UILabels into the cells of a UITableView. But when I am adding another data to the same label it's getting overwritten

The problem is that when in my programme I have to change the datas in that UILabels inside each cells. But when I tried changing it, the datas are getting over written. I can't remove the previously created labels from the contentview. I am working in Swift.
If you are creating those labels in code not in interface builder. You need to delete previous label before creating new one.
Better way of doing this is to do it in interface builder, subclass UITableViewCell, use your custom cell, drop a label in it, connect outlet and just override label data.
Go through this link. It will shows how to add UILabel in a custom cell.
Ok, I understand your problem. You must using reusable cell technique and you are creating your label every time. I don't know the syntax of Swift so showing you in Objective-C. Hope you can convert it into Swift.
In cellForRowAtIndexPath delegate
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
UILabel *myLabel = (UILable *)[[cell contentView] viewWithTag:1];
if(!cell)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
myLabel = [[UILabel alloc] initWithFrame:yourFrame];
[myLabel setTag:1];
}
[myLabel setText:#"your text"];
Try overriding the following method in your Cell.m to solve problem.
- (void) prepareForReuse {
NSLog(#"prep for reuse");
// set your lable to nil here
}

Making custom, dynamic tableview cells

I am struggling to figure out how to get complete control over my tableview cells. I want them to look something like this:
Right now I need to know how to properly manage my cells. Should I make a table view cells subclass? Should I be doing this all within the storyboard of the tableview? That's what I'm doing now. Also, how do I implement dynamic cell heights based on the amount of lines of text?
Thanks
You should subclass the UITableViewCell class and create your own custom cell using XIB. This will give you a lot of leg room for dynamism.
Refer to this tutorial for how to do so:
http://www.appcoda.com/customize-table-view-cells-for-uitableview/
U can create a custom view and use the followingin the cellForRowAtIndex
static NSString * cellIdentifier=#"MyTableView";
UITableViewCell * cell;
if(cell== nil)
{
cell = [myTableView dequeueReusableCellWithIdentifier:cellIdentifier];
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
contentCell.tag =100;
contentCell=[[ContentOfCell alloc]initWithFrame:CGRectMake(0, 0, 100, 50)];
[cell.contentView addSubview:contentCell];
}
else{
// Reusable part. Reuse the UI controls here from existing cell
contentCell = (ContentOfCell *)[cell.contentView viewWithTag:100];
}
//Assign all the data here
contentCell.nameField.text=[arr objectAtIndex:indexPath.section];
//same way for other fields
}
Where contentCell is a custom view
I will try to answer your question in three parts :
For Dynamic cell height which is based on text content : you have a table view delegate called heightForRowAtIndexPath, you should calculate the height of the text based on its font and font size characteristics, and of course by providing the available width, for this you can use method "sizeWithFont" of NSString.
For more control on the cell appearance : you should make a table view cell subclass and use it in the cellForRowAtIndexPath.
Should you be doing this using storyboard : It is not necessary to do it using storyboard.

How to get custom UITableViewCell to pick up user changes to preferred text size?

I'm working on a simple app to display items in a table view. If I return an ordinary UITableViewCell object from tableView:cellForRowAtIndexPath:
static NSString *cellIdentifier = #"EmailCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:
cellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"EmailCell"];
}
cell.textLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
... then the interaction with Dynamic Text works as expected; if the user goes to Settings | General | Text Size, changes the slider value and then returns to my app, all of the visible cells immediately update themselves to use the new font size.
If, however, I return a custom subclass of UITableViewCell, where the XIB contains a UILabel that is set to use a Text Style instead of a System Font, then the Dynamic Text does not work properly. Here is the code I'm using to assign the XIB to the table in viewDidLoad:
[self.table registerNib:[UINib nibWithNibName:#"EmailCell"
bundle:[NSBundle mainBundle]]
forCellReuseIdentifier:#"EmailCell"];
and then this code in tableView:cellForRowAtIndexPath:
static NSString *cellIdentifier = #"EmailCell";
EmailCell *cell = (EmailCell *)[tableView
dequeueReusableCellWithIdentifier:cellIdentifier];
When I first run the app, the visible cells appear with a text size that matches the user's selected preferred text size. However, if I then go to settings and change that size and then go back to my app, all of the visible cells remain with the previous text size. If I then scroll up, I will see two cells that show the new (correct) text size but the rest are still the old size. If I stop and restart the app, all cells now appear with the new (correct) text size.
It seems apparent to me that the tableview is keeping the previously-sized cells in the queue and not automatically updating their font size in response to the user's change of preferred text size. But I'm not understanding why the tableview does make this change automatically when the queue contains ordinary non-subclassed UITableViewCell instances. Is there any way I can get this to work without restarting the app (or without recreating the UITableView instance, thereby emptying the queue)? Is there any way to programmatically (and legally) clear this queue?
Edit: in case anyone's interested in this problem, my drop-in fix for this was to write a general utility method that makes a new tableview, copies over all the relevant properties from the original tableview (included registered cell classes) and then swaps the new one for the old one. Since this is a new table, it generates all-new instances of the queued cells which incorporate the new text size.
This is now handled for you in iOS 10.
http://useyourloaf.com/blog/auto-adjusting-fonts-for-dynamic-type/
Set adjustsFontForContentSizeCategory to YES / true on your label and it'll resize itself automatically when the text size preference changes.
Based on what you described, it would seem that you simply want to reload the table anytime the view comes back on screen after the user has backgrounded it. To achieve this the way I think you want, you need to add the following in your init method for your tableView - it will tell your tableView to reload the cells properly whenever the app is about to enter the foreground:
[[NSNotificationCenter defaultCenter] addObserver:self.tableView selector:#selector(reloadData) name:UIApplicationWillEnterForegroundNotification object:nil];
This way, if the user comes back to the view by opening the app after going to the phone's settings, your tableView should reload and the changes (if any were made) should properly be reflected.
You can see a quick video of the result I tested here:
https://www.dropbox.com/s/pvjuiyofydnxnvd/textsize.mov
EDIT:
Like you said in a previous comment, it would seem like it's something wrong with your nib implementation. The only difference is where/how you update the label property. In the custom cell, I created a label property and a font property, and added the label to the cell in init, and in layoutSubviews I overrode the font. Here's the code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
LabelCell *cell = [tableView dequeueReusableCellWithIdentifier:#"LabelCell"];
if (cell == nil) {
cell = [[LabelCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"LabelCell"];
}
cell.myLabel.text = _items[indexPath.row];
cell.myFont = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
return cell;
}
And in the cell itself:
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
self.myLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 0, self.contentView.frame.size.width - 20, 34)];
self.myLabel.backgroundColor = [UIColor greenColor];
[self.contentView addSubview:self.myLabel];
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
self.myLabel.font = self.myFont;
}
And here is the same result using custom cells with labels:
https://www.dropbox.com/s/ow2zkb6j9yq2c3m/labelcells.mov
Regarding "clearing the queue", the cells don't get queued up until they are juuuust about to be shown on screen, you can see this by logging a counter value right after you dequeue cell with identifier. If there are 10 cells on screen right now, it only dequeues 10 cells. This is the reason why you use the code if (!cell) {do stuff here that's common for all cells} and then you do things that are specific to each cell afterwards, and why this code works even if you were to assume that reloadData didn't "clear the queue", which I'm not convinced it wouldn't anyway after reading through the UITableView docs.
My drop-in fix for this was to write a general utility method that makes a new tableview, copies over all the relevant properties from the original tableview (included registered cell classes) and then swaps the new one for the old one. Since this is a new table, it generates all-new instances of the queued cells which incorporate the new text size.

UITableViewCell's contentView's content not visible when using UISearchDisplayController

I have written a simple contact manager application that uses a UITableView to display the contacts. Each contact is shown as a standard UITableViewCell; custom content is created as UIButtons and UILabels that are added as subviews of the cell's contentView. My table viewController's cellForRowAtIndexPath method includes:
UIButton *emailButton;
UITableViewCell *cell =
[theTableView dequeueReusableCellWithIdentifier:#"My Identifier"];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:identifier] autorelease];
emailButton = [UIButton buttonWithType:UIButtonTypeCustom];
[emailButton setImage:emailImage forState:UIControlStateNormal];
emailButton.tag = EMAIL_BUTTON_TAG;
emailButton.frame = emailButtonFrame;
[cell.contentView addSubview:emailButton];
} else {
emailButton = (UIButton *)[cell viewWithTag:EMAIL_BUTTON_TAG];
}
... set various attributes of the cell, including the content of custom labels
... added as subviews of the contentView exactly as above
This works fine when rendering my table. But I've also added a search bar to my app, set the search bar's controller appropriately, and set the controller's delegate back to this same tableController such that the exact same cellForRowAtIndexPath method is called when performing the searches, and of course I filter the set of cells to be displayed to match the query.
What I see is that when I perform a search, all of the content that I display by setting cell.textLabel.text or cell.imageView.image shows up perfectly in the table, but the emailButton or the labels that I added as subviews of the cell's contentView don't appear. In the debugger, I can clearly see that these controls (the buttons and labels) exist at the time that cellForRowAtIndexPath is called while search filtering is going on. But the controls don't render.
I feel there must be something very subtle in the interactions between table cells and the searchView, but I'm missing it.
Setting the textLabel's text property appears to also bring the textLabel to the front. Even though the text label does not appear to overlap with any of the content view's buttons, this is causing the buttons to disappear. Forcing them to the front after the textLabel is updated makes the problem go away.
It is not clear why this behavior is only appearing in the search case and not in the normal case, but I was able to reproduce it in a simple change to the iOS "TableSearch" example.
you can check if
identifier in *cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:identifier] autorelease];
is equalt to "My Identifier" from [theTableView dequeueReusableCellWithIdentifier:#"My Identifier"]; if not you can get empty cell, which you can use it's cell.imageView and cell.textLabel but does not have the contentView subviews.

Resources