I'm trying to add a subview to a UITableViewCell and the design that I'm working from demands that this particular subview (an image) needs to be larger than the actual UITableViewCell and thus partly overlap its siblings.
So I've set up my table cell, generated my image and added it to the cell's contentView:
// rowHeight for the UITableView is 45.0f
UIImage *image = [self createCellThumbnail: someImage];
UIImageView *thumbView = [[UIImageView alloc] initWithFrame: CGRectMake(150, -5, 55,55)];
thumbView.transform = CGAffineTransformMakeRotation(0.1f);
thumbView.image = image;
cell.clipsToBounds = NO;
cell.contentView.clipsToBounds = NO;
[cell.contentView addSubview: thumbView];
While the image will 'overflow' into the cell below it, the top of the image is always clipped, as demonstrated here:
Does anyone know if what I'm trying to do is possible with the current approach?
Or should I just figure out a way to draw these images onto the UITableView after all the cells are drawn (it's a non-scrollable tableview, so that would work and be fairly easy).
Update:
Have also tried adding the following, to no avail:
cell.opaque = NO;
cell.contentView.opaque = NO;
cell.clearsContextBeforeDrawing = NO;
cell.contentView.clearsContextBeforeDrawing = NO;
cell.clipsToBounds = NO;
cell.contentView.clipsToBounds = NO;
I seems that the tableView renders its cell from bottom to top, so the cells above one cell overlap that one cell. To avoid this, you'd have to set the backgroundColor of all cells to +[UIColor clearColor] so that you won't see those overlap problems.
But setting the backgroundColor to clear in -tableView:cellForRowAtIndexPath: does not make any sense. UIKit does a lot of stuff with the cell before it's drawn, so does it reset the backgroundColor property of the cell.
What we need to do is setting the backgroundColor in a later state. Luckily there is this -[UITableViewDelegate tableView:willDisplayCell:forRowAtIndexPath:] which we can implement like this:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
cell.backgroundColor = [UIColor clearColor];
}
Now we're setting the backgroundColor just before the cell is drawn an this turns out to be working.
UPDATE:
So I've done some more experimentation and the following solution still works without having to set the background of the cell to transparent, this involved moving the z order of the covered cell. This works with highlighting and selecting of the other cell (via the relevant callbacks), and if the two cell's backgrounds are different colors. Solution is as follows (you can ignore the didHighlight and didSelect methods if they don't matter to you):
(note that "covered row" is the one whose content we are trying to keep visible and In my case its content goes slightly into the row above, which was clipping it)
-(void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 0 && indexPath.row == ROW_ABOVE_COVERED_ROW)
{
NSIndexPath * rowbelow = [NSIndexPath indexPathForRow:indexPath.row+1 inSection:indexPath.section];
UITableViewCell* cell = [tableView cellForRowAtIndexPath:rowbelow];
[cell.superview bringSubviewToFront:cell];
}
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 0 && indexPath.row == ROW_ABOVE_COVERED_ROW)
{
NSIndexPath * rowbelow = [NSIndexPath indexPathForRow:indexPath.row+1 inSection:indexPath.section];
UITableViewCell* cell = [tableView cellForRowAtIndexPath:rowbelow];
[cell.superview bringSubviewToFront:cell];
}
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 0 && indexPath.row == COVERED_ROW)
{
[cell.superview bringSubviewToFront:cell];
cell.contentView.superview.clipsToBounds = NO;
}
}
NOTE: you should also set the background color of your content to clear, or it will adopt the bgcolor of the rest of your cell, and so when you manage to bring your content to the front of the covering cell, it will take the background color with it and leave a nasty looking block in the other cell (in my case my only content was the detailTextLabel and the textLabel):
// in cellForRowAtIndexPath:
[cell setBackgroundColor:[UIColor redColor]]; //using red for debug
cell.detailTextLabel.backgroundColor = [UIColor clearColor];
cell.textLabel.backgroundColor = [UIColor clearColor];
I hope that's helpful to anyone else trying this....
ORIGINAL:
For me the solution was to use:
self.contentView.superview.clipsToBounds = NO;
My cells were already transparent, but my content was still getting clipped. In my case I was using a custom cell which moves it's content up in layoutSubviews. So layoutSubviews for my custom cell wound up as follows:
-(void)layoutSubviews
{
[super layoutSubviews];
self.contentView.frame = CGRectOffset(self.contentView.frame, 0, -11);
self.contentView.superview.clipsToBounds = NO;
}
I don't know if this would work if the cell above was opaque, or if the cells were to highlight when pressed, whether this would cover up my content.
However, I didn't need to make the cell transparent again in the viewWillDisplayCell callback method - doing it in the normal cellForRowAtIndexPath was sufficient
I had this problem and I made sure my custom tableviewcell's main background had clip subviews checked and it solved the problem. This was with a custom tableview cell loaded from a xib though. Not exactly the same but similar situation.
I actually had the opposite just yesterday, I had created a custom table cell and for some reason I got an overflow which I didn't want to have. My solution was to add the following code to my view controller class:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 175;
}
When it matched the height of the table cell there was no overlap; when it was too small there was overlap. Mind you though that I got very quicky behavious so I'm not sure it's a very good idea to do this.
Related
Trying to select the multiple cells in UICollectionView and when scrolled the selection c hanges. Why it is happening, please guide.
Below is code. i tried.
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
printf("Selected View index=%d",indexPath.row);
itemPaths = [self.collectionView indexPathsForSelectedItems];
UICollectionViewCell* cell=[self.collectionView cellForItemAtIndexPath:indexPath];
cell.contentView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"blue_s.png"]];
}
- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell* cell=[self.collectionView cellForItemAtIndexPath:indexPath];
cell.contentView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"yellow_seat.png"]];
}
What else should i try.
You can do one thing to solve this problem you can give tag to each cell using custom cell of UICollection View so that it will have unique identity
Since you are likely reusing your cells, when you hand the cells back out (cellForItemAtIndexPath) you need to check if the indexPath is selected and color up your cell. You will likely want to decolor your cells too since the reuse function may send back a colored up cell in some cases when it should not.
This is driving me absolutely insane.
I have a UITableView with cells populated via an NSFetchedResultsController that should have their background color set based upon one of the Core Data parameters.
This table view is in the master view of a UISplitViewController and the selected cell needs to remain visibly selected to indicate what is being displayed in the detail view.
Based upon guidance from several other Stack Overflow questions, I have learned that the ideal place to configure the cell is during the willDisplayCell delegate call, like this:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
WorkTask *workTask = (WorkTask*) [self.fetchedResultsController objectAtIndexPath:indexPath];
if ([workTask.strStatus isEqualToString:#"A"]) {
cell.backgroundColor = [self colorWithHexString:#"fffdcf"];
// cell.textLabel.backgroundColor = [self colorWithHexString:#"fffdcf"];
// cell.detailTextLabel.backgroundColor = [self colorWithHexString:#"fffdcf"];
} else if ([workTask.strStatus isEqualToString:#"B"]) {
cell.backgroundColor = [self colorWithHexString:#"cfffd1"];
// cell.textLabel.backgroundColor = [self colorWithHexString:#"cfffd1"];
// cell.detailTextLabel.backgroundColor = [self colorWithHexString:#"cfffd1"];
} else if ([workTask.strStatus isEqualToString:#"C"]) {
cell.backgroundColor = [self colorWithHexString:#"ffcfcf"];
// cell.textLabel.backgroundColor = [self colorWithHexString:#"ffcfcf"];
// cell.detailTextLabel.backgroundColor = [self colorWithHexString:#"ffcfcf"];
} else {
cell.backgroundColor = [self colorWithHexString:#"ffffff"];
// cell.backgroundColor = cell.contentView.backgroundColor;
}
This mostly sort of works. But...
Depending on how I play around with different variants of accomplishing this, I end up with the background color being ignored sometimes (and only sometimes?!?) behind the textLabel and detailTextLabel. Or causing the cell to display incorrectly while selected. Or having the checkmark indicator displayed without a background color. Or having new items added to the core data database showing up in the table, but with the no background color for the cell, but the text labels having a background color.
No matter what I do, I have not found a simple and intuitive way to make things behave overall as expected - particularly when cells are being programmatically selected.
In fact - the cell selection seems like it might be at the root of the problem. The selected cell is usually the one that ends up drawn incorrectly after I change the selection to another, particularly if the color of the cell changed while the cell was selected.
Is there any example out there anywhere of how this is supposed to work?!?!
Thanks!
If I were you I would create a UITableViewCell subclass with your own titleLabel/subtitleLabel UILabels in it and stop using the textLabel/detailTextLabel. In the xib file you can just change the background color of the labels and of the cell. I have never had the sort of problem you are experiencing when I have used custom cells as opposed to the default cells.
Here's an example of how to load a UITableViewCell from an xib:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CustomCell"];
if (cell == nil) {
// Load the top-level objects from the custom cell XIB.
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
// Grab a pointer to the first object (presumably the custom cell, as that's all the XIB should contain).
cell = [topLevelObjects objectAtIndex:0];
}
cell.titleLabel.text = #"whatever";
cell.subtitleLabel.text = #"whatever";
return cell;
}
You could also try setting the background color of the cell's contentView.
cell.contentView.backgroundColor = ...;
If you really can't figure it out, then in a UITableViewCell subclass you could always just put a UIView of your own in the background of the cell and change the background color of that view instead.
willDisplayCell:forRowAtIndexPath: might not get called after insertRowsAtIndexPaths: is called (i.e. when you add items into core data w/ a fetchedresultscontroller). If absolutely necessary maybe you should try setting the background color of the cell and it's contentView in both cellForRowAtIndexPath: and willDisplayCell:forRowAtIndexPath:.
For some bizarre reason the background color of UITableViewCell objects can only be set just before the cell is drawn. In your UITableView's delegate implement this method:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
Set the background color of the cell there and it will draw the way you want.
I've got these cells I have set a custom background colour to. The background colour works fine when I select the cell, however, when I scroll down and back up, two things can happen:
If not many cells are selected, the cells that went out of view sometimes come back with the default blue colour when selected.
If most or all of the cells are selected, the cells that went out come back with one of the colours that is on the cells that were there beforehand - ie. I select all the cells, scroll down and back up and the cells at the top have the same colour as the cells at the bottom (or at least some of them - others retain their own colour).
Here is the code I have that produces this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *row = [tableView cellForRowAtIndexPath:indexPath];
UIView *backview = [[UIView alloc] initWithFrame:row.frame];
backview.backgroundColor = [colours objectAtIndex:indexPath.row];
row.selectedBackgroundView = backview;
}
That's where the selected method for the cells changes the colour. The cells are created here:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"eventTypeID";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSString *sEventType = [[self.eventTypes valueForKeyPath:#"name.text"] objectAtIndex:indexPath.row];
cell.textLabel.text = sEventType;
return cell;
}
And the colours for each cell are set here:
- (void)loadView {
colours = [[NSMutableArray alloc] init];
CGFloat red[] = {0.84,0.86,0.7,0.46,0.56,0.44,0.95,0.91,0.91,0.76,0.06,0.8,0.73,0.0,0.0,0.01,0.18,0.23,0.57,0.18};
CGFloat green[] = {0.12,0.01,0.07,0.17,0.32,0.18,0.49,0.49,0.78,0.61,0.48,0.85,0.85,0.28,0.38,0.53,0.23,0.36,0.32,0.24};
CGFloat blue[] = {0.34,0.5,0.2,0.53,0.55,0.31,0.18,0.18,0.12,0.27,0.14,0.1,0.49,0.1,0.37,0.49,0.4,0.41,0.55,0.40};
for (int i = 0; i < 20; i++) {
[colours addObject: [UIColor colorWithRed:red[i] green:green[i] blue:blue[i] alpha:1.0]];
}
//Get data from server and parse it
...
}
Now, I have only just started programming the iPhone but my guess (and this is a wild one btw) is that the cells are getting re-created in cellForRowAtIndexPath and although some of the properties are getting saved (like the title...) the custom background isn't.
Has anyone come across this behaviour before? If so, how did you solve it?
EDIT: Even weirder behaviour: Sometimes, if you scroll back down and up, the cell that had gone to the "default" selected background colour goes back to it's custom one. The behaviour seems to be random.
Cell background colours are set in many places, to ensure that the background displayed is the one you want you need to use:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
see this question for more details. If you require custom selection colours, then you should subclass UITableViewCell and override - (void)setSelected:(BOOL)selected and - (void)setHighlighted:(BOOL)highlighted
I have a variable that is going to keep track of how many cells need to be colored. So if that variable is 3, then the top three cells backgroundcolor will change. How can I do this?
I know I need to update this in
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
But how can I ensure the top cells have a different background color based on my variable?
The indexPath parameter is your starting point. If coloredCells is an integer that holds the number of cells you are coloring, your method would include something like
- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath {
// fetch or create the cell, first
UITableViewCell *cell = // ...
// then set it up
if(indexPath.row < self.coloredCells) {
cell.contentView.backgroundColor = [UIColor redColor];
} else {
cell.contentView.backgroundColor = [UIColor whiteColor];
}
// perform rest of cell setup
// ...
return cell;
}
Now, if you adjust the value of coloredCells, you'll need to inform the table view that some of its views have changed. The laziest way to do that is to reload the whole table:
// elsewhere...
self.coloredCells = 4;
[self.tableView reloadData];
Or you can take a little more effort to reload just the cells that have colored backgrounds:
self.coloredCells = newColoredCount;
NSMutableArray *indexPaths = [NSMutableArray arrayWithCapacity:newColoredCount];
for(int i = 0; i < newColoredCount; i++) {
[indexPaths addObject:[NSIndexPath indexPathForRow:i inSection:0]];
}
[self.tableView reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
You would test for the row number and change the color accordingly. In cellForRowAtIndexPath use:
//having allocated or recycled a cell into myCell pointer above
//test for row and assign background color like so
if (indexPath.row < 3) {
myCell.contentView.backgroundColor = [UIColor greenColor];
} else {
myCell.contentView.backgroundColor = [UIColor redColor];
}
//continue configuring your cell
You can use the tableView:willDisplayCell:forRowAtIndexPath: of your UITableViewDelegate to change things like the background colour of the cell. Changes made in cellForRowAtIndexPath may be lost before the cell is actually displayed, so it's usually better to do it in this method.
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
if(indexPath.row < numberOfColoredRows) {
cell.backgroundColor = [UIColor redColor];
}
}
From the discussion of this method in the reference:
A table view sends this message to its delegate just before it uses
cell to draw a row, thereby permitting the delegate to customize the
cell object before it is displayed. This method gives the delegate a
chance to override state-based properties set earlier by the table
view, such as selection and background color. After the delegate
returns, the table view sets only the alpha and frame properties, and
then only when animating rows as they slide in or out.
No, you don't have to update the tableView:cellForRowRowAtIndexPath: delegate method. All you have to do is this:
[self.tableView cellForRowAtIndexPath:indexPath].backgroundColor = desiredUIColor;
Note that calling tableView:cellForRowAtIndexPath: from type id<UITableViewDataSource> is different from calling cellForRowAtIndexPath: from type UITableView. The former calls the delegate method (this should never be directly called) and the latter returns the current cell for an index path without recalculating the cell.
If you only have one section in your table view, the math to calculate the top n cells is easy. If your "variable that is going to keep track of how many cells need to be colored" is (NSUInteger)numberOfHighlightedCells, this is some simple loop code your could run:
NSUInteger i;
for (i = 0; i < numberOfHighlightedCells; i++) {
[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]].backgroundColor = desiredUIColor;
}
However, if you have more than one section in your table, some very complicated calculating of index paths may be necessary.
I have a UItableView (grouped style). I wanted to customize it so that it appears like a "Stick it" or a "Post it" with a yellow background interface.
Hence is added the following code
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UIView *backgroundView = [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
backgroundView.backgroundColor = [UIColor yellowColor];
cell.backgroundView = backgroundView;
cell.textLabel.backgroundColor=[UIColor yellowColor];
}
This actually worked. I was able to achieve the yellow background, but the separator line is missing. Can some1 help me out ?
Is this the best way to do it? Or should i create an image for the cell and use it as the backgroundView?
Actually, none of the above answers are (I believe) correct. According to the doco:
Note: If you want to change the background color of a cell (by setting the background color of a cell via the backgroundColor property declared by UIView) you must do it in the tableView:willDisplayCell:forRowAtIndexPath: method of the delegate and not in tableView:cellForRowAtIndexPath: of the data source. Changes to the background colors of cells in a group-style table view has an effect in iOS 3.0 that is different than previous versions of the operating system. It now affects the area inside the rounded rectangle instead of the area outside of it.
So rather than call setBackgroundColor in tableView:cellForRowAtIndexPath:, you should be doing so in tableView:willDisplayCell:forRowAtIndexPath::
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
ShoppingList *object = (ShoppingList*)[self.fetchedResultsController objectAtIndexPath:indexPath];
if ([object isComplete] == YES) {
[cell setBackgroundColor:[UIColor greenColor]];
} else {
[cell setBackgroundColor:[UIColor clearColor]];
}
}
This I've found works perfectly, and fits in with the way the rest of the table cell rendering works. There's no need for any background views to be added or anything.
I'm actually surprised that worked to set the background color. The proper method to setting a UITableViewCell background is:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
cell.backgroundColor = [UIColor yellowColor];
}
Also, I notice you don't have any reuse code in your cellForRowAtIndexPath method. I'm hoping you just didn't paste this in, but that's kind of a big no-no. Table View Programming Guide
Outdated.
Why are you creating a custom background view? If you only want to achieve a yellow color you can customize your color by using UIColor colorWithRed: green: blue: alpha: to create your own color and just add it to cell setBackgroundColor:UIColor. It seems like the seperator is being completely overwritten if you add a background view in grouped table view. If you still want to have a custom UIView as your background, I suggest adding a custom tableViewCell that looks like your seperator. You can regulate this in cellForRowAtIndexPath.
Working code for my first suggestion:
[cell setBackgroundColor:[UIColor yellowColor]];
Does this work for you to put inside "cellForRowAtIndexPath"?
....................
if (cell == nil) {
....................
cell.backgroundColor = [UIColor yellowColor];
....................
tableView.backgroundColor=[UIColor clearColor];
}
Set the separatorStyle of the tableview to UITableViewCellSeparatorStyleSingleLine.
Should be something like below.
myTableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;