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.
Related
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
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
}
This has been driving me mad, I have look at about every post on around in regard to this.
I am using custom tableview cell classes and XIB files. It all works fine, until I scroll the table view off screen (that old chestnut). I know it has to do with the dequeueReusableCellWithIdentifier method and not clearing the old data. However I have no idea how to implement this.
Once the cell scrolls off screen., it causes an overlap and continues to add images to the cell.
let cell: DetailHeaderTableViewCell = tableView.dequeueReusableCellWithIdentifier("detailHeaderTableViewCell") as DetailHeaderTableViewCell
From what I have found, I need to implement something like this:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:nil];
UIImageView *defaultImageView;
UILabel *customLabel;
if (cell == nil) {
// create the cell and empty views ready to take the content.
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
} else { ... }
However I cannot work out how to achieve this in swift.
Many thanks in advance.
What you're looking for is UITableViewCell's prepareForReuse method.
If you're using custom UITableView cells, you need to implment this method to clear out any old data from your cells before they are reused.
I'm new in iOS programming that's why I'm looking for the most efficient solution to my problem.
What I want to achieve is to display in UITableViewCell with a name (some text) and under each name some filled little rectangles with a number inside, similar to badges.
My first idea is to create a UIView that will represent the badge and in a custom UITableViewCell I will add these rectangles as subviews.
The second idea is to create only one UIView that will draw all the little rectangles.
My question is, which is the better performing solution knowing that:
the number of cells will be max. 20 and the total number of rectangles no more than 50
The number of rectangles displayed in a cell is different
I want to reuse the cells, so I have to update/redraw the cell content for each row
I want to avoid the cell selection view problem that "hides" the subviews
Of course any other solution is appreciated.
Thanks in advance,
hxx
What i would suggest is to sub class the UITableViewCell and make the customization u need in it.The customized view can have a label and rectangles below it.
The rectangles can be small custom buttons with background images (if you have any or give it a background color) and title as your number.You would have to ,however calculate their width based on the width of your table to accomodate the maximum number of rectangles.
You can disable the selection of the table in the xib or you can do it programmatically like so cell.selectionStyle = UITableViewCellSelectionStyleNone; and do not implement didSelectRowAtIndexPath
I have followed the approach of subclassing the cell for my tables to customize their look and feel and it works good.I hope this helps.
A Good tutorial to begin with subclassing can be found here
http://howtomakeiphoneapps.com/how-to-design-a-custom-uitableviewcell-from-scratch/1292/
Why you are not creating cell in -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath Here you can defines your custom type cell which will also reuse and whenever you want you can add the different thing to cell like this.
static NSString *CellIdentifier = #"Cell";
UILabel *RequestSentTo;
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] ;
cell.selectionStyle=UITableViewCellSelectionStyleNone;
RequestSentTo = [[UILabel alloc] initWithFrame:CGRectMake(11, 2, 286, 40)];
RequestSentTo.backgroundColor = [UIColor clearColor];
RequestSentTo.tag = 200;
RequestSentTo.numberOfLines = 3;
RequestSentTo.font=[UIFont boldSystemFontOfSize:15.0];
RequestSentTo.textColor = [UIColor blackColor];
RequestSentTo.lineBreakMode=UILineBreakModeWordWrap;
[cell.contentView addSubview:RequestSentTo];
} else {
RequestSentTo=(UILabel*)[cell.contentView viewWithTag:200];
}
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:#"Shift Request for "];
[string appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:#"%# by ",dateStr] attributes:nil]];
[string appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:#"Dr. %#",notificationsObj.doctorName] attributes:purpleTextAttributes]];//purpl
RequestSentTo.attributedText=string;
RequestSentTo.lineBreakMode=UILineBreakModeWordWrap;
RequestSentTo.numberOfLines = 3;
Whenever you want you can add the things you want with reusing cell. Hope this helps
2 methods come into my mind.
You can put the components as subview inside UITableViewCell(Through XIB or programatically subclassing UITableViewCell) and use it in UITableView.
You can subclass UITableViewCell, and override the -(void)drawRect method and draw all the components that you wish to be displayed on cell.
See if can help.
You can create a new class extends to UITableViewCell, which means to rewrite UITableViewCell as your own cell named as MyTestCell.
And in this Cell you call create your properties, like labels and views, and add those to your new cell.
like add this to MyTestCell.h
#property (nonatomic, retain) UILable *myLable1;
#property (nonatomic, retain) UIView *mySubview1;
MyTestCell.m
_myLable1 = .....
_mySubview = .....
[self addSubview: _myLbale1];
[self addSubview: _mySubview1];
And when use, u can work like this
static NSString *CellIdentifier = #"MyCell";
MyTableViewCell *cell = [tableview dequeReuseID:CellIdentifier];
if (cell == nil) {
cell = [MyTableViewCell alloc] init.........
}
//And you can sign your property here in your cell
cell.myLable1 = ....
cell.myView1 = .....
return cell;
}
If your strings add to the lable is different,make the lable.height is different. you can use code like this
CGSize labelSize = [str sizeWithFont:[UIFont boldSystemFontOfSize:17.0f]
constrainedToSize:CGSizeMake(280, 100)
lineBreakMode:UILineBreakModeCharacterWrap]; //check your lableSize
UILabel *patternLabel = [[UILabel alloc] initWithFrame:CGRectMake(35, 157, labelSize.width, labelSize.height)];
patternLabel.text = str;
patternLabel.backgroundColor = [UIColor clearColor];
patternLabel.font = [UIFont boldSystemFontOfSize:17.0f];
patternLabel.numberOfLines = 0;// must have
patternLabel.lineBreakMode = UILineBreakModeCharacterWrap;// must have
add this to your cell, and make it dynamically resize your lable as well as your cell! And also you have to dynamically set high for your tableView Row height.(Do know what is dynamically resize?)
See this:
rewrite the method setMyLable1 in MyTableViewCell.m
-(void)setMyLable1:(UILable*)aLable
{
//in here when never your sign your alabel to your cell (like this : cell.myLable1) this method will be call and u can get the size of your string and set this label's height
//get string size StringSzie
[_myLable1 setFrame:CGRectMake(10,10,stringSize.width,stringSize.height)];
//And resize your cell as well
[self setFrame:CGRectMake(0,0,_myLable1.frame.size.width+20,_myLable1.frame.size.height+20)];
//done!!!
}
OK you get a automactically reszie cell for yourself and you have to dynamically reset height for your row in tableView too!!!!!
What do you need is called custom cell
Here is good tutorial for it
customize table view cells for uitableview
Previously I had this set up with a storyboard, having dragged the UILabels, positioned them and sized them whatnot on the UITableViewCell I dragged them onto, and then do a different version of that for the other UITableViewCell.
For example, like follows (but in the picture they've yet to be customized with the labels):
Then in the datasource, I'd simply check the Identifier, and depending on what the Identifier was, customize the cell accordingly.
However, I've needed more customization than I can get from the storyboard, as each cell is going to have two UIViews (a top one and a bottom one to allow sliding of the top one) so I can't really do this with storyboarding, as I add the labels and everything to the UIView programmatically.
But my question is: When I do it programmatically, how can I tell which cell is which so I can customize the layout of the UILabels accordingly? With a storyboard I can obviously just drag a UILabel onto each one, but when doing it programmatically and setting up the UIView, I don't know how to say, "Hey, if the identifier is this, add the UILabels like so" because the UIViews aren't aware of any Identifiers.
Basically the structure looks like this:
UITableView -> UITableViewCell -> CellFront(UIView) & CellBack(UIView)
And the look of the cell comes from the labels added to the CellFront UIView. But there's two looks to the cells and I don't know how to do it without a storyboard.
Although UIViews are not aware of identifiers, they have a property called tag which can be used for any purpose that you would like. You can set the tag to, say, 1 on cells of one kind, and to 2 on cells of the other kind, and then use the tag to distinguish the cells in code. Moreover, once your views are tagged, you can call viewWithTag: on the containing view, and get back the view with the tag that you want.
If you are creating the cells solely in code, then you register your UITableViewCell subclass in the viewDidLoad method of your table view controller. That method sets the identifier. Then, you use that identifier in cellForRowAtIndexPath: just like you would for a xib or storyboard created cell.
[self.tableView registerClass:[MyCellSubclass class] forCellReuseIdentifier:#"MyIdentifier"];
Here is one approach:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Adjust the condition to match your needs
if (indexPath.row == 0) {
static NSString *Identifier1 = #"CellType1";
// cell type 1
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:Identifier1];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:Identifier1];
// add subviews here
}
// set cell properties
return cell;
} else {
static NSString *Identifier1 = #"CellType2";
// cell type 2
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:Identifier2];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:Identifier2];
// add subviews here
}
// set cell properties
return cell;
}
}