Note: I'm using iPhone SDK 3.0 Final
Version for Leopard.
I need to prepare a table listing similar to the one used by iPhone's Photo Album application. There are two new styles for UITableViewCell:
UITableViewCellStyleValue1
UITableViewCellStyleValue2
I thought i can use these instead of creating a custom cell class, but looks like they don't work properly. I'm facing following issues with UITableViewCellStyleValue2:
I'm unable to show an image.
If i change the font size of textLabel and detailTextLabel, the values are not aligned.
Here's the sample code:
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell.
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.imageView.image = [UIImage imageNamed:#"Pointy.gif"];
cell.textLabel.text = #"Label";
cell.textLabel.textColor = [UIColor redColor];
cell.textLabel.font = [UIFont systemFontOfSize:21];
cell.detailTextLabel.text = #"(10)";
cell.detailTextLabel.textColor = [UIColor grayColor];
cell.detailTextLabel.font = [UIFont systemFontOfSize:17];
return cell;
}
This piece of code work with UITableViewCellStyleDefault and UITableViewCellStyleSubtitle to show images. Does UITableViewCellStyleValue2 support cell image property? I see blank space in place of image area (left corner of the cell). Am i doing something wrong here?
In case this doesn't work, how can i build a custom cell class with a style similar to UITableViewCellStyleValue2 style. I'm particularly interested in getting the detailTextLabel left aligned with textLabel.
Thanking in anticipation.
The new builtin styles layout sets of fields with configurations that are the same as a number of the system layouts (like the cells in the AddressBook).
As to your exact specific:
UITableViewCellStyleValue2 doesn't have an image
The builtin styles layout logic assume the font metrics are what they are set to. If you change those the fields won't line up
You can get a detailed decription of what is available from each of the builtin styles in the documentation. If none of those set your needs you need to subclass UITableViewCell, add your own subviews, and layout them out in layoutSubviews. Apple has A large number of examples of how to the TableViewSuite.
Related
I have been looking everywhere and have not quite found my answer.
I populating a UITableView with dynamic cells from JSON and I am trying to hide any extra cells. I turned off the separators in IB, and of course all the cell separators disappear. How do I add a line to the bottom and top of each tableviewcell so that only the cells that have information show a border? I have imported Quartz and have been playing with CALayer but can't find a solution.
I found a similar question here, but the only answer was not very helpful.
What would be a better, different way of doing this?
Here are my cellForRowAtIndexPath and my numberOfRowsInSection:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
//set equal to the information in the array
return [_jsonDataArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
//create Dictionary of data in row
NSDictionary *jsoninfo = [_jsonDataArray objectAtIndex:indexPath.row];
//get required keys from dictionary and assign to vairables
NSString *title = [jsoninfo objectForKey:#"title"];
NSString *subtitle = [jsoninfo objectForKey:#"subtitle"];
NSURL *imageURL = [NSURL URLWithString:[jsoninfo objectForKey:#"series_image_URL"]];
//download the images.
NSData *imgData = [NSData dataWithContentsOfURL:imageURL];
UIImage *img = [[UIImage alloc] initWithData:imgData];
//set boarder for custom cells... I need to have a border on the top and bottom of the cells I am creating so xcode does not autofill the empty space.
//fill in text to cells
cell.textLabel.text = title;
cell.detailTextLabel.text = subtitle;
cell.imageView.image = img;
return cell;
}
I also think it's not the best idea, but if you really want to do this, here's code that will achieve what you want:
tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
// Draw top border only on first cell
if (indexPath.row == 0) {
UIView *topLineView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 1)];
topLineView.backgroundColor = [UIColor grayColor];
[cell.contentView addSubview:topLineView];
}
UIView *bottomLineView = [[UIView alloc] initWithFrame:CGRectMake(0, cell.bounds.size.height, self.view.bounds.size.width, 1)];
bottomLineView.backgroundColor = [UIColor grayColor];
[cell.contentView addSubview:bottomLineView];
Put this code in the tableView:cellForRowAtIndexPath: method. The final look of your UITableView will be like this:
Take into account that this is not very good for performance, especially if you have a lot of cells. If you have a bigger amount of data, refer to this SO question for help on how to optimize the drawing.
Only try this at tableView:cellForRowAtIndexPath: method
[cell.contentView.layer setBorderColor:[UIColor grayColor].CGColor];
[cell.contentView.layer setBorderWidth:1.0f];
It sounds like a sub-optimal solution to try to "distinguish" your valid cells from empty ones with lines. A superior approach would be to clean up the data source before populating the table with it.
This is not the answer to the question, but the clean solution for the original problem.
Add an empty UIView as the footer of the UITableView. Then the empty cells will be hidden. See this answer.
Use Custom Cells. Your Datasoure (Models) should drive the information into the Custom Cells. Create a setter within the Custom Cell class that can be set at each row. as in....
Allocation your Custom Cell with reuse Id,
Pass the property that is determing if the line should show:
[cell setCustomLines:Model.property];
return the cell;
You will have far more flexibility to design the CustomCell any way you want, Images, Lines, Colors, or other ways of letting your user's see a difference among your cells.
Technically, Marco's Answer will work, and good job on a simple solution. But you will not be able to expand this very much farther this this.
I have a uitableview with uitableviewcells however some of my text is so long its going right to the edge of the cell, where my accessory tick view is going to be when selected..
I am currently wrapping the text if it extends beyond the width and it goes onto the second line.. However I was hoping there is a nice way to restrict the width of this UITableViewCell label.
Inside your cellForRowAtIndexPath: function. The first time you create your cell:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
cell.textLabel.numberOfLines = 0;
cell.textLabel.font = [UIFont fontWithName:#"Helvetica" size:17.0];
}
Now specify how large your UITableViewCell will be, so do that in your heightForRowAtIndexPath function:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *str = [[arrTexts objectAtIndex:indexPath.row]; // filling text in label
CGSize maximumSize = CGSizeMake(300, 100); // change width and height to your requirement
CGSize strSize = [str sizeWithFont:[UIFont fontWithName:#"Helvetica" size:17] constrainedToSize:maximumSize lineBreakMode:UILineBreakModeWordWrap] //dynamic height of string depending on given width to fit
return (10+strSize.height+10) // caculate on your bases as u have string height
}
In cellForRowAtIndexPath Set the width of the textLabel
cell.textLabel.frame = CGRectMake(cell.textLabel.frame.origin.x, cell.textLabel.frame.origin.y, 280, cell.textLabel.frame.size.height);
thats all.
I think you need subclass UITableViewCell to override -layoutSubviews... call super, then frame your label appropriately. Constraints my be of some help, but I haven't used them on iOS yet. (Assuming the problem is the built-in layout function make the label as wide as it needs to be to accommodate your text)
There are two ways to do so.
1) Create a custom UITableViewCell class, add your UILabels with whatever position & size you want. And then use it in cellForRowAtIndexPath method.
2) Else, don’t use default UILabels of UITableViewCell ie. textLabel & detailTextLabel, add your own UILabels in cell with whatever position & size you want.
The code below has code that determines the frame size of a UILabel, and I think it does work, however when I place it within my rowAtIndexPath for a UItable I get wonky results.
Perhaps, I dont fully understand how or what the reuseIdentifier does, but I placed the code to calculate the frame only when the cell is nil. What happens is that the heights are calculated only for the first three cells, then it repeats in sequence for the rest of the cells. For example, cell one's height is used for cell four's height.
Maybe someone can point me in the right direction as to how I should setup my calculations.
Thanks!
if(cell == nil){
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:DisclosureButtonCellIdentifier] autorelease];
//start adding custom subviews to the table cell
//addSubview for Description
UILabel *descValue = [[UILabel alloc] init];
NSString *descString = rowData.summary;
CGSize maximumSize = CGSizeMake(185, 130);
UIFont *descFont = [UIFont fontWithName:#"HelveticaNeue" size:12];
CGSize descStringSize = [descString sizeWithFont:descFont
constrainedToSize:maximumSize
lineBreakMode:descValue.lineBreakMode];
CGRect descFrame = CGRectMake(125, 60, 185, descStringSize.height);
descValue.frame = descFrame;
descValue.backgroundColor = [UIColor redColor];
descValue.font = descFont;
descValue.tag = kDescriptionValueTag;
descValue.lineBreakMode = UILineBreakModeWordWrap;
descValue.numberOfLines = 0;
[cell.contentView addSubview:descValue];
[descValue release];
}
UILabel *desc = (UILabel *)[cell.contentView viewWithTag:kDescriptionValueTag];
desc.text = rowData.summary;
Using NSString UIKit Additions, you can get the width of a string using a particular font:
[myString sizeWithFont:myFont];
There are several other functions in that Additions set that can help figure out how big a piece of text will be, with different layout options, for single and multi-line text, etc.
The purpose of the reuseIdentifier is to let you reuse a cell -- with particular subviews in particular places -- without the device having to spend the execution time to do all that layout. Definitely more useful back in the iPhone 1 days when the processor was much slower. Until you are more familiar with reuseIdentifiers, I would suggest you just create a new cell every time that function is called.
Each time the OS calls your cellForRowAtIndexPath, you need to fill out the content correctly. Anything that needs to get resized or changed depending on the row should be set.
Frankly, I did not try understanding your code completely. That is nearly impossible especially because you close the method with brackets without returning anything but cellForRowAtIndexPath which I assume you are referring to, requires the return of a UITableViewCell object.
Apparently we are looking only at some fraction of the code.
However, layouting a cell properly is not the full task. You need to tell the table the height of each cell.
You may want to implement
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
for that task. That method is good if the cell height varies.
If the cell hight is different from the standard but does not vary from cell to cell then setting of the rowHeight property of the tableView will be sufficient.
I think that in your case the implementation of the method heightForRowAtIndexPath is mandatory.
BTW, you may want to look at subclassing the UITableCellView and implement the layoutSubviews method in your subclass.
You didn't show all of your code but usually to implement tableView:cellForRowAtIndexPath: you start with the following:
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// get a cell from the queue of reusable cells, if any
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: DisclosureButtonCellIdentifier];
if (cell == nil) {
// No cell to reuse, need to create a new one
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:DisclosureButtonCellIdentifier] autorelease];
// add cell subviews here
}
// fill in cell content and resize subviews here
...
return cell;
}
Once you have created 3 (in your case) cells they get reused as the table scrolls so you don't have to keep creating cells. The call to dequeueReusableCellWithIdentifier will return one of the previously created cells if it was not longer in use (it scrolled off the top or the bottom).
I have a UITableView in which of course I use some UITableViewCells. Now some cells have an icon / image which I want to display in front of the text, and some others don't. I didn't create the UITableViewCells in Interface Builder, just using the default stuff :
// Customize the appearance of table view cells.
- (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] autorelease];
}
// Set up the cell...
switch (indexPath.row) {
case 0:
cell.textLabel.text = #"Nearby";
cell.imageView.image = [UIImage imageNamed:#"marker.png"];
break;
case 1:
cell.textLabel.text = #"Bookmarked";
cell.imageView.image = [UIImage imageNamed:#"bookmark.png"];
break;
case 2:
cell.textLabel.text = #"Other";
break;
default:
break;
}
return cell;
}
This seems to be working perfectly, but I have a little issue with the width of the cell.imageView. The icons I'm using seem to have a different width. Some are 21 pixels, others are 25 pixels and in some casts I have no icon at all.
This results in the text for the textLabel to appear at different positions (depending on the width of the imageView).
Now my question is, can I add some code / statement which will make sure that the imageView of my cell is always 30 pixels wide ? Even if there is no image for that imageView ?
Can this be done programatically or will I have to make a Custom Cell in InterfaceBuilder or even create my own UITableViewCell descendant.
Any information on this topic is welcome.
Regards,
Stefaan
The imageView property in a table cell is readonly, however, the imageView's properties are as they would normally be, so you can manipulate its width via it's frame property.
I'm almost certain that setting an empty imageView's width isn't going to help. I believe that if an imageView doesn't contain an image, the table cell doesn't add it as a subview...
On cells that don't have an image, you could try setting the frame of the textLabel so that it's origin is where the other textLabels would be if images were present? Remember that if you do that, you'll have to shorten the textLabels width too.
A slightly easier but hackier way:
1) Make sure your images are the same width. Use an image editor like Acorn to pad the images with a transparent area.
2) Make a transparent .png 30 (or whatever) pixels wide. Set this as the image on rows with no icon.
3) There's no step three!
I'm new to Objective-C and iPhone coding and was hoping someone could help me.
Basically I want a scrollable table that displays a name and a quantity of that item. For example:
Apples.........2
Oranges......4
Bananas......5
I want the name to be left-justified and the number to be right-justified.
Is there any way to do this with a UITableView or will I need to extend UIScrollView?
Thanks in advance.
Set cell.text to "Apples". Then create an new UILabel view with the text "2" and assigned it to cell.accessoryView.
Text is always on the left, and accessory views are always on the right. Optionally a UIImage in cell.image will be displayed to the left of the cell text if you choose to add one.
If you have a cell layout more complicated than that you have to add subviews to your cell manually.
One not too difficult option is to subclass UITableViewCell with one label for the text and one label for the number, then just set the label values.
If you want the ... that you've shown then you could probably use a standard UITableViewCell and write some code to feed the cell text that has been pre-modified to fit in the space alloted.
You could simply use another CellType, which is already provided by apple and change the color for the detailTextLabel:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CustomCellIdentifier = #"Cell";
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CustomCellIdentifier];
cell.textLabel.text = #"Apples";
cell.detailTextLabel.text = #"2";
cell.detailTextLabel.textColor = [UIColor blackColor];
return cell;
}
No need to bother with subclassing or adding labels on your own.