I'm trying to figure out how to place a subview to a custom UITableViewCell with centered position. As stated in the documentation (Customizing Cells) one should "...add subviews to the contentView property of the cell object or ...". As I noticed it works fine with UITableViewStylePlain. In UITableViewStyleGrouped tables, subviews can not be placed centered. They are shifted to the right hand side.
If the subview is added to UITableViewCell directly, it's fine with both modes. The same is true, if I subclass the UITableViewCell and load the cell from XIB.
Unfortunately, I can't upload screenshots. Can anyone help fix the issue? Or do I miss something vital, as the misalignment seems to match the dimension to be shrunken in grouped table.
Many thanks,
El
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier1 = #"AddToCell";
static NSString *cellIdentifier2 = #"AddToContentView";
static NSString *aVeryLongLine = #"---------------------------------------------";
UITableViewCell *cell = nil;
if (indexPath.section == 0) {
cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier1];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:cellIdentifier1] autorelease];
CGRect frame = cell.frame;
CGRect labelFrame = CGRectMake((frame.size.width-270)/2, (frame.size.height-40)/2, 270, 40);
UILabel *label = [[UILabel alloc] initWithFrame:labelFrame];
label.textAlignment = UITextAlignmentCenter;
label.text = aVeryLongLine;
[cell addSubview:label];
[label release];
}
}
else if (indexPath.section == 1) {
cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier2];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:cellIdentifier2] autorelease];
CGRect frame = cell.contentView.frame;
CGRect labelFrame = CGRectMake((frame.size.width-270)/2, (frame.size.height-40)/2, 270, 40);
UILabel *label = [[UILabel alloc] initWithFrame:labelFrame];
label.textAlignment = UITextAlignmentCenter;
label.text = aVeryLongLine;
[cell.contentView addSubview:label];
[label release];
}
}
else {
}
return cell;
}
Instances of UITableViewCell resize their content views under a number of circumstances (for example, when temporarily adding a delete button to allow the user to remove a row).
When cells are displayed in a grouped style table view, they're resized to provide a margin between the cells and the left and right edges of the table view. So for your subviews to stay positioned correctly, you'll need to set their autoresizing masks appropriately, something like this:
mySubview.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
(or
[mySubview setAutoresizingMask:(UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleLeftMargin |
UIViewAutoresizingFlexibleRightMargin)];
for folks who prefer the old syntax.)
Did you check the frame's value, which come from cell.contentView.frame or cell.frame ?
When the cell is initialized with style and identity, it maybe have a CGZeroRect for his frame value.
Related
I am loading custom separator image in uitableview cell.
Here is my code :
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
static NSString *CellID=#"Cell"
MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:SwitchCellIdentifier];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
UIImageview *aSwitch = [[UIImageview alloc] initWithImage:[UIImage imageNamed:#"divider.png"]];
separator.frame = CGRectMake(0,50,320,1);
[cell.contentView addSubview:seperator];
}
if(cell.height == 22)
{
/// here i am setting frame for uiimageview
}
but i am getting seperator image disappears for only one row out of 20 while scrolling.
Can you please help why it is loading like this.
If you've put your separator UIImage out of the bounds of the cell, and set cell.clipsToBounds = NO; to have the separator image displayed, the separator image might get hidden by drawing the next cell.
You can't control the z-index of the cells as they're being drawn on screen, it depends from where you're scrolling (bottom to top, or top to bottom).
If that's indeed you're issue, you can either put the divider inside the cell's frame, or if your separator is thin enough use:
[TableView setSeparatorColor:[UIColor colorWithPatternImage:[UIImage imageNamed:...]]];
self.tblView=[[UITableView alloc] initWithFrame:CGRectMake(0,0,320,370) //set as u need
style:UITableViewStylePlain];
self.tblView.delegate=self;
self.tblView.dataSource=self;
[self.view addSubview:self.tblView];
UIView *v = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 10)];//set as u ne
v.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"divider.png"]];
[self.tblView setTableHeaderView:v];
[self.tblView setTableFooterView:v];
[v release];
I have a UITableView with a title and an image on each cell. Some cells will have a default image and others will not. When I scroll the table, the image of some rows is not the expected and the image of another row gets displayed instead of the expected one. If I don't use dequeuereuseidentifier everything works fine, but I want to use it because I have lots of cells.
Any suggestion?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyCell"];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"MyCell"];
CGRect titleRect = CGRectMake(60, 6, 200, 24);
UILabel *title = [[UILabel alloc] initWithFrame: titleRect];
title.tag = 1;
title.backgroundColor = [UIColor clearColor];
title.font = [UIFont fontWithName:#"AdelleBasic-Bold" size:15.5];
[cell.contentView addSubview:title];
UIImageView *defaultCellImage = [[UIImageView alloc] initWithFrame:CGRectMake(8, 10, 42, 42)];
defaultCellImage.tag = 2;
[defaultCellImage setImage:[UIImage imageNamed: #"Default_Row_Image"]];
[cell.contentView addSubview:defaultCellImage];
}
NSUInteger row = [indexPath row];
Movie *movie = [_movies objectAtIndex: row];
UILabel *titleRowLabel = (UILabel *) [cell.contentView viewWithTag:1];
titleRowLabel.text = [movie title];
UIImageView *cellImage = (UIImageView *) [cell.contentView viewWithTag:2];
if (![movie.imageName isEqualToString:#""])
[cellImage setImage:[UIImage imageNamed: [movie imageName]]];
return cell;
}
The first cells to be used in your table view will be properly loaded. Since there is no cell to dequeue, the if (cell == nil) will return YES and your cell will have its image set to be the default. Then, if your condition for setting a different image is satisfied later in the method, a different image will be shown. So far, so good.
However, when a reusable cell is dequeued, it already has an image set, which might not be the default. Since cell == nil will now return NO, this cell will never have its image reset to the default one, even if it is the image that should be shown.
First question on this site, although I have been around for a while behind the scenes. I have a problem that I have been racking my head on for the last two days and I hope someone can shed some light on it for me.
I have a UITableView, which is loaded from a SQL database. It has 15 entries in it. I have inserted an extra cell at the beginning of the UITableView. This extra cell is for a UITextField and UIButton which adds an item into the database.
When the view is loaded, the first cell with the custom objects shows fine, and the rest of the table is filled with items from the database and looks just how it should. However, when the UITableView is scrolled down so the first cell is out of view, then back up, it takes the value of the 11th row item and shows it over top the first cell.
Here is my code:
- (UITableViewCell *)tableView:(UITableView *)popTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
static NSInteger NameTag = 1;
UITableViewCell *cell = [popTableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
CGRect frame;
frame.origin.x =50;
frame.origin.y =10;
frame.size.height =22;
frame.size.width =275;
UILabel *nameLabel = [[UILabel alloc] initWithFrame:frame];
nameLabel.tag = NameTag;
nameLabel.opaque = YES;
nameLabel.textColor = [UIColor grayColor];
nameLabel.backgroundColor = [UIColor clearColor];
[cell.contentView addSubview:nameLabel];
[nameLabel release];
}
int row = [indexPath row];
if (row == 0) {
UIButton *buttonLeft = [UIButton buttonWithType:UIButtonTypeCustom];
[buttonLeft setFrame: CGRectMake( 205, 6, 40, 33)];
[buttonLeft addTarget:self action:#selector(addToList:) forControlEvents:UIControlEventTouchUpInside];
[cell addSubview:buttonLeft];
//No Alloc for txtField, it is built in IB
[txtField setBorderStyle:UITextBorderStyleNone];
[txtField setFrame: CGRectMake( 17, 12, 180, 23)];
txtField.backgroundColor = [UIColor clearColor];
[txtField addTarget:self action:#selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
txtField.clearButtonMode = UITextFieldViewModeAlways;
}else{
UILabel * nameLabel = (UILabel *) [cell.contentView viewWithTag:NameTag];
Add *theObj = [self.theArray objectAtIndex:indexPath.row - 1];
[nameLabel setText:theObj.itemName];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
UIImageView *imageView = [cell viewWithTag:kTagCellImageView];
if (imageView == nil) {
imageView = [[UIImageView alloc]initWithFrame:CGRectMake(10.0, 10.0, 13.0, 25.0)];
imageView.backgroundColor = [UIColor clearColor];
imageView.tag = kTagCellImageView;
[cell.contentView addSubview:imageView];
}
if([theObj.itemName isEqualToString:#"First Street"]){
imageView.frame = CGRectMake(14,10,13,25);
[imageView setImage:[UIImage imageNamed:#"firststreet"]];
}
else if([theObj.itemName isEqualToString:#"Second Street"]){
imageView.frame = CGRectMake(8,12,29,20);
[imageView setImage:[UIImage imageNamed:#"second"]];
}
else if([theObj.itemName isEqualToString:#"Main Street"]){
imageView.frame = CGRectMake(15,10,13,25);
[imageView setImage:[UIImage imageNamed:#"mainstreet"]];
}
else{
imageView.frame = CGRectMake(8,8,25,25);
[imageView setImage:[UIImage imageNamed:#"iconcustom"]];
}
NSLog(#"%#",itemName);
NSLog(#"%#",itemCategory);
}
return cell;
}
Also here is my cellForRow:
- (NSInteger)tableView:(UITableView *)popTableView numberOfRowsInSection:(NSInteger)section {
return [self.theArray count] + 1; //Add Extra cell to beginning
}
Any ideas would be greatly appreciated.
You need to use a different reuseIdentifier for your first cell. Try this:
NSString *cellIdentifier;
if (indexPath.row == 0) {
cellIdentifier = #"first";
} else {
cellIdentifier = #"not first";
}
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
// .. cell initialization
}
Obligatory tangential answer - have you thought about setting the tableHeaderView on the UITableView instead? I think that'd accomplish what you're trying to do in a cleaner way (in that it adds an arbitrary view to the top of the table).
Just create a UIView with your "add a new item" controls in it and then set it as the header view when first creating the table.
The issue is here
static NSString *CellIdentifier = #"Cell";
static NSInteger NameTag = 1;
UITableViewCell *cell = [popTableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
you are dequeueing all of the cells with the same identifier. Row 1 (index 0) needs to have its own CellIdentifier. Also it looks like you keep adding subviews to the same cells that you dequeue. On your if(cell == nil) check you may want to decide if you want to remove all of the cells contentView subviews or reuse them.
I am creating an application in which i have many table cells(>300). I have successfully implemented expanding and collapsing tableview cells. On expansion an image is displayed which is fetched from a webservice, on clicking the expanded cell, the cell collapses again, if another cell is clicked the previous cell collapsed and current cell expands. Now the problem is if the user scrolls the tableview with the cell expanded table works fine but when he returns
to it the image gets lost, and there is a bit of problem with the functionality as well( the table takes that cell to be collapsed).
Moreover if the user keeps on scrolling the table down he can encounter that image in a collapased cell(which looks very bad), i think its because dequeueReusableCellWithIdentifier is fetching the already expanded cell.
PS: Every image is different based on the cell value but it is fetched in a different call(not al together ie to fetch 300 images i make 300 independent calls).
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UIImageView * imageView;
UILabel * nameLabel;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(45.0, 5.0, 245.0, 34.0)];
nameLabel.font = [UIFont fontWithName:CELL_FONT size:CELL_FONT_SIZE];
nameLabel.tag = 2;
nameLabel.backgroundColor = [UIColor clearColor];
[cell.contentView addSubview:nameLabel];
[nameLabel release];
imageView = [[UIImageView alloc] initWithFrame:CGRectMake(5.0, 5.0, 30.0, 30.0)];
imageView.tag = 1;
imageView.contentMode = UIViewContentModeScaleAspectFit;
[cell.contentView addSubview:imageView];
[imageView release];
}
else {
imageView = (id)[cell.contentView viewWithTag:1];
nameLabel = (id)[cell.contentView viewWithTag:2];
}
return cell;
}
I have been looking for a solution to this issue for a few days now... and cannot find someone with a similar problem or a solution that would work for me. At this point I am not even sure that I am doing something wrong, as I have read and analyzed many sample code and I am almost 100% sure that I am doing this the way it should...
Anyway here it comes:
I have a UITableView to which I display custom built UITableViewCell, here is where I create them:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"CellIdentifier";
NSDictionary *myDictionary = [myArray objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero
reuseIdentifier: CellIdentifier] autorelease];
CGRect textViewRect = CGRectMake(10.0, 0.0, 250, 25);
UILabel *textView = [[UILabel alloc] initWithFrame:textViewRect];
textView.text = [myDictionary objectForKey:#"name"];
textView.font = [UIFont systemFontOfSize:12.0];
textView.textColor = [UIColor blackColor];
textView.highlightedTextColor = [UIColor whiteColor];
textView.textAlignment = UITextAlignmentRight;
[cell.contentView addSubview:textView];
[textView release];
CGRect imageRect = CGRectMake(270.0, 15, 16, 16);
UIImageView *icon = [[UIImageView alloc] initWithFrame:imageRect];
icon.image = [myDictionary objectForKey:#"color-icon"];
icon.highlightedImage = [myDictionary objectForKey:#"gray-icon"];
[cell.contentView addSubview:icon];
[icon release];
}
So as you can see, pretty standard stuff... Then when I click on one of the cell, another view gets loaded instead of the table.
Until now, everything is fine, but then when I come back to that table view and that it has to reload the problem starts...
By the way I have added this to the delegate methods so the cells never stay selected:
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
// [...] a lot more code here
}
When I clicked on my cell in the simulator with the mouse and keep it down the cell stays selected as expected (same way on the device itself) and this is why I get:
[Image 1]
The UILabel Text is totally mixed up ! The screenshot is actually of the last cell of the table and the overlapping text is the one of the first cell. And the behavior on the first is similar, if I maintain the click on it, it will show the same behavior mixing up with the UILabel text of the last cell. I am guessing that is is due to the way the dequeueReusableCellWithIdentifier works (probably a FILA queue).
Of course I tried to work around that and found some really weird stuff.
If I don't click the cells are perfect, no bugs, the correct text is displayed etc:
[Image 2]
Then I tried to mess up a little bit with the parameters of my UILabel. I added this:
textView.backgroundColor = [UIColor clearColor];
And when I do this, as soon as the table reload, then I don't even need to highlight the cell to see the screw up behavior:
[Image 3]
The only way I was about to get rid of the problem to always instantiate a new cell rather than dequeueing one...
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero
reuseIdentifier: CellIdentifier] autorelease];
was replaced by:
UITableViewCell *cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero
reuseIdentifier: CellIdentifier] autorelease];
But of course this won't work on the long term because of the memory leak...
Also, it is to be noted that I have the exact same behavior for another UITableView in another view somewhere in this App... The other custom cell is more complex, more labels, more images, etc, but all the label exhibit the same behavior. And this more complex Table View is managed by a Navigation Controller, so no custom loading / unloading like the first one...
That's all I have, and I can't find a solution... please help !
Arghh this is really annoying... I am new so I cannot post images... :(
Here is a link where you can see the referenced images: https://skitch.com/aponsin/rne9k/fullscreen.png-100-layer-3-rgb-8
Alex
The problem is that you create new label and add it to cell each time cell is being reused. To fix that you must create your label only when your cell is created:
if (cell == nil){
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero
reuseIdentifier: CellIdentifier] autorelease];
CGRect textViewRect = CGRectMake(10.0, 0.0, 250, 25);
UILabel *textView = [[UILabel alloc] initWithFrame:textViewRect];
textView.tag = kLabelTag;
textView.font = [UIFont systemFontOfSize:12.0];
textView.textColor = [UIColor blackColor];
textView.highlightedTextColor = [UIColor whiteColor];
textView.textAlignment = UITextAlignmentRight;
[cell.contentView addSubview:textView];
[textView release];
CGRect imageRect = CGRectMake(270.0, 15, 16, 16);
UIImageView *icon = [[UIImageView alloc] initWithFrame:imageRect];
icon.image = [myDictionary objectForKey:#"color-icon"];
icon.highlightedImage = [myDictionary objectForKey:#"gray-icon"];
[cell.contentView addSubview:icon];
[icon release];
}
And after that you can get your label from cell using tag property you've assigned:
UILabel *textView = (UILabel*)[cell.contentView viewWithTag:kLabelTag];
textView.text = [myDictionary objectForKey:#"name"];
same logic also applies to setting up your icon imageview if it varies in different cells
Also have a look at components standard UITableViewCell already provides (depending on its cell style) - there's already UIImageView and UILabels there and you can set your custom properties to them and use them without creating extra components