I have set up a custom UITableViewCell with multiple labels and an image, and I've (finally) got it to look how I want it to look.
I can't seem to figure out how to get the selection to look how I want it to look, however. I have implemented the setSelected method, which allows me to change the background color just fine, but what I would really like is to set the background color to black and display a colored rectangle on the left-hand side of the selected cell (10 pixels wide and the height of the cell).
The colored box would be a color set programmatically, so although I could easily set the selectedBackgroundView to be a UIImageView, this will not work in this case.
The following code will not display the selectedViewColor UIView at all when the cell is selected:
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
UIView *selectedView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.selectedBackgroundView.bounds.size.width, self.selectedBackgroundView.bounds.size.height)];
[selectedView setBackgroundColor:[UIColor blackColor]];
UIView *selectedView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, self.selectedBackgroundView.bounds.size.height)];
[selectedViewColor setBackgroundColor:[UIColor redColor]];
[selectedView addSubview:selectedViewColor];
self.selectedBackgroundView = selectedView;
[super setSelected:selected animated:animated];
}
This code seems pretty basic, so I assume there is an issue with displaying any type of subview within the selectedBackgroundView.
Any alternatives or advice would be greatly appreciated.
There's a few things that could be done better with this code. Reinitialising two views in the setSelected method is pretty inefficient. You're actually blanking out everything in the cell when you select it with this code (which I'm guessing is not what you want). And finally, you're treating selectedBackgroundView as if it's the only view that gets displayed when you select the cell (according to Apple's documentation, it is displayed over the backgroundView).
Try the following (Edited) -
Put this code where you create the cell (presumably, cellForRowAtIndexPath:)
UIView* container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, cell.backgroundView.bounds.size.width, cell.backgroundView.bounds.size.height)]; // we need this because in cells, the background views always take up the maximum width, regardless of their frames.
container.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0]; // make it transparent - we only want the square subview to be seen.
UIView *selectedViewColor = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, self.selectedBackgroundView.bounds.size.height)];
[selectedViewColor setBackgroundColor:[UIColor redColor]];
[container addSubview:selectedViewColor]
cell.selectedBackgroundView = container;
This will make your red square appear when (and only when) the cell is selected, over the other views in the cell. From Apple's docs:
UITableViewCell adds the value of this property as a subview only when the cell is selected. It adds the selected background view as a subview directly above the background view (backgroundView) if it is not nil, or behind all other views.
Second, use the following code in your cell:
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
if(selected == YES)
{
self.backgroundView.backgroundColor = [UIColor blackColor];
}
else
{
self.backgroundView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0] // replace this with whatever's appropriate - a return to the unselected state.
}
}
This will ensure that your background turns black when the cell is selected (without otherwise interfering with what's displayed. Hopefully these changes should also resolve the issue you're having.
In addition to Xono's answer above, following up on his comment on the answer above:
One thing I've come across a couple of times while researching this subject is a possibility that the code behind UITableViewCell may actually set the backgroundColor of all subviews to transparent when a cell is selected.
It does indeed do this if your cell's SelectionStyle is anything but None. And it does it in both the setHighlighted and setSelected calls of UITableViewCell.
My first solution was to subclass UITableViewCell and override both these methods, not calling the base class method and doing my own animations. This is not ideal as your now re-implenting (probably badly) standard iOS animations.
Looking into it further though, the standard methods animate the opacity of the whole view, not the subviews (they only set the color of the subviews). So the best approach is to still call the base class methods, and then just re-set your subview colors back to whatever they should be. Even though your setting them instantaneously, because their superview is still animating its opacity, it still fades in and out correctly:
public override void SetHighlighted(bool highlighted, bool animated)
{
base.SetHighlighted(highlighted, animated);
SelectedBackgroundView.Subviews[0].BackgroundColor = SelectionColor;
}
public override void SetSelected(bool selected, bool animated)
{
base.SetSelected(selected, animated);
SelectedBackgroundView.Subviews[0].BackgroundColor = SelectionColor;
}
This is c# MonoTouch code, but it applies equally well to obj-c.
Note that in my case I always have exactly 1 subview, hence the hardcoded 0 indexer. This may differ depending on your view structure.
You can override method "setBackgroundColor" for your subviews and add another method with the same functionality. At this case UITableCell won't be able to change backgroundColor after selection.
- (void)setBackgroundColor:(UIColor *)backgroundColor {
}
- (void)setColor:(UIColor *)color {
[super setBackgroundColor:color];
}
Basing on #Xono 's answer, I made this solution to the problem:
Inside of the initWithStyle:reuseIdentifier: method I added a separator view:
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
CGRect selectionViewFrame = self.contentView.bounds;
UIView *selectionView = [[UIView alloc] initWithFrame:selectionViewFrame];
[selectionView setBackgroundColor:[UIColor colorWithWhite:1. alpha:0.65]];
self.selectedBackgroundView = selectionView;
self.vwSelectedSeparator = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.contentView.frame.size.width, 1.)];
[self.vwSelectedSeparator setBackgroundColor:[UIColor colorWithHexString:#"aaa"]];
[self.vwSelectedSeparator setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
[selectionView addSubview:self.vwSelectedSeparator];
[self setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
}
return self;
}
And then in setSelected:animated: method I added these lines:
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
if (selected) {
[self.vwSelectedSeparator setBackgroundColor:[UIColor colorWithHexString:#"aaa"]];
}
}
Works like a charm for both ios6 and ios7 for me. I hope this helps.
Related
In my app I'm using dark table and cells. I've set everything to black in my storyboard, and when it all works perfect until I add a Search Display Controller. The search box itself is the desired color, but when I pull down, I see a scroll view that is white. I cannot find anywhere in the storyboard settings to change that background color. I've checked several places to include searching this site in depth. Any help would be greatly appreciated.
I've gone as far as calling this helper I found on stackoverflow from viewDidLoad and programmatically setting all backgrounds to black. It still shows up as white. I've tried this for the self.view self.tableView self.searchDisplayController and still no dice.
- (void)listSubviewsOfView:(UIView *)view {
// Get the subviews of the view
NSArray *subviews = [view subviews];
[view setBackgroundColor:[UIColor blackColor]];
for (UIView *subview in subviews)
{
[subview setBackgroundColor:[UIColor blackColor]];
NSLog(#"%#, %#", [subview class], subview.backgroundColor);
[self listSubviewsOfView:subview];
}
}
Accidentally I found a solution to this problem. If you set the backgroundView property of the table view to anything, for example:
self.tableView.backgroundView = [[UIView alloc] init];
the white background disappears and backgroundColor is visible as expected.
Use this method .. it will call when you start searching..
- (void)searchDisplayController:(UISearchDisplayController *)controller didLoadSearchResultsTableView:(UITableView *)tableView
{
tableView.backgroundColor = [UIColor anyColor];
}
You need to remember that while searching, another table view is loaded by the searchDisplayController.
So you may need to set the backgroundColor of the searchResultsTableView, like this:
[self.searchDisplayController.searchResultsTableView setBackgroundColor:[UIColor purpleColor]]; //Purple couldn't be a good choice :)
What I am trying to do is display a smaller view on top of a UITableView when a UITableViewCell is clicked. I don't want to transition to another UIViewController but "popup" a view on top of the UITableView that will house more information about the UITableViewCell clicked.
I don't think I am looking for a UIAlertView, but I am looking for a UIView that I can put labels, buttons, pictures, etc. on.
I hope my terminology is correct. I am still a newb :)
Leon
p.s. All my searching just came up with UIAlertView stuff.
Try using didSelectRowAtIndexPath delegate function of the UITableView, the code is untested to give you an idea:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Assuming view is at zero index of XIB file.
// this view will contain all lable and other controls
UIView *customView = (UIView *)[[NSBundle mainBundle] loadNibNamed:#"NameOfCustomViewXIBFile" owner:nil options:nil] objectAtIndex:0];
customView.transform = CGAffineTransformMakeScale(0.0f, 0.0f);
[self.view addSubView:customView];
[UIView animateWithDuration:0.5
animations:
^{
customView.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
}
];
}
Hope it helps!
EDIT:
Animation to remove this popup:
customView.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
[UIView animateWithDuration:0.5
animations:
^{
customView.transform = CGAffineTransformMakeScale(0.0f, 0.0f);
}
];
[customView removeFromSuperView];
The problem with the existing answer is that if the user scrolls the table so that the cell goes offscreen and back on, the view that you popped up is likely to be gone due to cell re-use. To avoid this, you must store something in the cell's "model" so that every time cellForRowAtIndexPath: is called, you re-generate the cell with its popup view if it is supposed to be there. Just displaying from didSelectRowAtIndexPath: may be insufficient.
The approach I'd prefer for this if I were coding it is to make all the cells your own subclass of UITableViewCell. This subclass includes an extra BOOL showPopup. In your setter setShowPopup: you set the value, and also create & show or destroy/remove/hide the "popup" subview for that cell. (You could make the subview a class member that's always around, allocate and assign it when needed, and keep a reference, just showing/hiding as needed; that's a size/space performance trade-off.)
-(void) setShowPopup:(BOOL show) {
showPopup = show;
if(show) {
// create subview, and add to view
} else {
// remove and destroy subview
}
}
And in UITableView delegate/datasource:
-(UITableViewCell) UITableView:(UITableView *table) cellForRowAtIndexPath:(NSIndexPath *ip) {
// usual stuff to get a reusable cell or allocate a new one, and prepare it for display
// then
if(showPopup) {
// create subview and add to view
} else {
// remove and destroy subview
}
}
I used a UIButton for something similar in a GLKViewController so this might work for a UITableViewController. I just disabled the button and added a rounded border so it looked like a custom popup. When the user clicked on something interesting, I changed the hidden flag on the button to show or hide it.
Something like this to make it look pop-up-y
In viewDidLoad:
// make info "popup"
_infoBtn = [UIButton buttonWithType:UIButtonTypeCustom];
_infoBtn.bounds = CGRectMake( 0, 0, kRightButtonBarDim, kRightButtonBarDim);
_infoBtn.backgroundColor = [UIColor colorWithWhite:1 alpha:0.75];
_infoBtn.selected = NO;
_infoBtn.titleLabel.font = [UIFont boldSystemFontOfSize:kTableCellFontSize];
_infoBtn.titleLabel.lineBreakMode = NSLineBreakByCharWrapping;
[_infoBtn setTitleColor:[UIColor colorWithWhite:.3 alpha:1] forState:UIControlStateDisabled];
// border
_infoBtn.layer.cornerRadius = 5; // rounded corners
// drop shadow
_infoBtn.layer.masksToBounds = NO;
_infoBtn.layer.shadowColor = [UIColor blackColor].CGColor;
_infoBtn.layer.shadowOpacity = 0.8;
_infoBtn.layer.shadowRadius = 12;
_infoBtn.layer.shadowOffset = CGSizeMake(12.0f, 12.0f);
_infoBtn.enabled = NO;
_infoBtn.hidden = YES;
[self.view addSubview:_infoBtn];
Then in tableView's didSelectRowAtIndexPath update any information needed in the button and set hidden to NO. You should be able to add any necessary subviews to this UIButton.
_infoBtn.frame = CGRectMake(20, 20, 20, 20); // set this wherever you like
[_infoBtn setTitle:#"text" forState:UIControlStateDisabled];
_infoBtn.hidden = NO;
I just tested this for a UIViewController that has a UITableView as a subview and it looked the same. Scrolling did not affect it (i.e., the button did not move with scrolling, almost like a HUD) because the button was added to the top-level UIView. Dunno if this is what you need, but hopefully it will give you some ideas.
I have a UITableView which when edited looks as follows :
Is it possible to somehow change the background behind the delete symbol so that it is not white ?
I hope this help you
UIView *cellBackView = [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
cellBackView.backgroundColor = [UIColor clearColor];
cell.cellBackgroundView = backView;
The problem ist that contentView gets shifted to the right when you are in editing mode, and because of this all its subviews will move to the right as well.
If your background is an imageView you should not add is as subview to contentView, set it as backgroundView of the cell instead.
Since you can't setup backgroundView from interface builder I would recommend to create a custom subclass of your cell and put the background creation into awakeFromNib.
- (void)awakeFromNib {
[super awakeFromNib];
self.backgroundView = [[UIImageView alloc] initWithImage:...];
}
My UITableViewCells are all pre-defined "Subtitle" style. I want to set the background image for a selected cell to another picture. I tried every which way to implement this and all methods discussed on stackoverflow seem to fail.
I tried again for some other, easier way to change the selectedBackgroundView property, like:
cell.selectedBackgroundView = [[UIView alloc] initWithFrame:cell.bounds] ;
cell.selectedBackgroundView.backgroundColor = [UIColor yellowColor];
But it doesn't work as well. What's wrong with that ?
As I understand you want to set selected background image to your cell?
cell.selectedBackgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"backgroundImage.png"]];
EDIT:
As i know UITableViewCell can not be highlighted after selection in such common cases:
There is somewhere set cell.selectionStyle = UITableViewCellSelectionStyleNone;
You implemented willSelectRowAtIndexPath and it returns nil;
There is set [self.tableView setAllowsSelection:NO];
May be you set self.tableView.userInteractionEnabled = NO; or cell.userInteractionEnabled = NO;
You subclassed UITableViewCell and implemented such methods not correct setSelected:animated: or setHighlighted:animated
May be share your cellForRowAtIndexPath method code to investigate the problem
You can change the highlight color in several ways.
Change the selectionStyle property of your cell. If you change it to UITableViewCellSelectionStyleGray, it will be gray.
you can also check that property in your tableView:didSelectRowAtIndexPath:
//do something like this for color
cell.selectionStyle= UITableViewCellSelectionStyleGray;
// for image
cell.selectedBackgroundView=[[UIImageView alloc]initWithImage:[UIImage imageNamed:#"imageName"]];
Change the selectedBackgroundView property. Actually what creates the blue gradient is a view. You can create a view and draw what ever you like, and use the view as the background of your table view cells.
best of luck..
In my case the problem appeared after compiling on Xcode 13 - sdk iOS 15+
with any change to the code. Eventually I subclassed the cell and add the code:
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
[self.contentView insertSubview:self.selectedBackgroundView
atIndex:0];
self.selectedBackgroundView.hidden = !selected;
}
I have a custom UITableView with a UIImageView as its background. I want to display some transparent cells in the table so that I can see the background image through the cell's background image.
So far, I'm setting the cell's backgroundView to a UIImageView with a custom alpha and setting the background image view's backgroundColor to [UIColor clearColor]. I'm also setting the cell's backgroundColor to [UIColor clearColor].
When the cell is initially drawn, I still see a white background. However, if I drag the cell off the screen, it's transparent when it comes back on. Anyone have any idea what's up with that?
Edit: Here's some code, though I don't think it really tells you anything more than what I said above. Some sensitive/irrelevant stuff is edited out.
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
if((self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])) {
[self setBackgroundColor:[UIColor clearColor]];
...
_backgroundImageView = [[UIImageView alloc] initWithFrame:[[self contentView] bounds]];
[_backgroundImageView setAlpha:0.5];
[_backgroundImageView setOpaque:NO];
[_backgroundImageView setBackgroundColor:[UIColor clearColor]];
[self setBackgroundView:_backgroundImageView];
}
return self;
}
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
[_backgroundImageView setFrame:rect];
}
// in a different file
- tableView:cellForRowAtIndexPath: {
...
[cell setBackgroundColor:[UIColor clearColor]];
[cell setOpaque:NO];
return cell;
}
Have you called setNeedsDisplay?
remove this
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
[_backgroundImageView setFrame:rect];
}
add this
-(void)layoutSubviews{
[_backgroudImageVIew setFrame:self.contentView.bounds];
}
why are you doing this:
[_backgroundImageView setAlpha:0.5];
Figured out the problem. This was actually due to a weird interaction of UITableView with the cells, not the cells themselves. As I mentioned, I'm using a custom table view with a UIImageView background, but I made the mistake of delaying setting the table view's backgroundView property to the image view until drawRect: instead of doing it in init or awakeFromNib or anywhere else that would make sense.
Setting the background view of the table view actually changes what the cells do for some reason. Previously, when I was changing the background view in drawRect:, the cells would be drawn as if the table view had a white background, then update when they got redrawn. Now that I've moved things into an initializer method everything's fine.
Sorry, I didn't provide code for the table view because it didn't even occur to me that it could be causing the problem (nor did it occur to me that I was an idiot and put a line of code in the wrong method... don't code at 4 AM :/)