In my tableView I set a separator line between cells. I am allowing selection of multiple cells. Here's my code for setting selected cell background color:
UIView *cellBackgroundColorView = [[UIView alloc] initWithFrame:cell.frame];
[cellBackgroundColorView setBackgroundColor:[UIColor darkGray]];
[cell setSelectedBackgroundView:cellBackgroundColorView];
The problem is that if two adjacent cells are selected, there is no separator line between them in iOS7, while there is (as expected) in iOS6.
I even tried setting cellBackgroundColorView's frame height to that of cell.frame - 1.0, but that doesn't work either.
Any ideas?
I haven't gotten to the bottom of it yet (at first glance it seems like an iOS 7 bug..), but I have found a workaround. In tableView:didSelectRowAtIndexPath, if you send both messages below, the issue is visually resolved (with the probable performance cost).
[tableView deselectRowAtIndexPath:indexPath animated:YES];
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
For this to work (for me), deselectRowAtIndexPath:animated: must contain animated:YES. The animation used for reloadRowsAtIndexPaths:withRowAnimation: doesn't matter.
Add this code at cell for row at indexpath
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.textLabel.backgroundColor = [UIColor clearColor];
in my case i was animating a row, so just i needed put some like this:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView beginUpdates];
[tableView deselectRowAtIndexPath:indexPath animated:NO];
//if you are doing any animation you have deselect the row here inside.
[tableView endUpdates];
}
#samvermette's answer solved the issue for me, But I had to deselect the selected Row first.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//Deselect Row
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
// fix for separators bug in iOS 7
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
self.tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine; }
I encountered this issue when I set my cell's selection style to none programatically, and then when I SELECT my table cells programatically.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: UITableViewCell!
if tableView == self.jobLevelTableView {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellIdentifier", for: indexPath) as! CheckboxCell
// for testing purposes
let checked = true
// I used M13Checkbox here, in case anybody was wondering
cell.checkbox.setCheckState(checked ? .checked : .unchecked, animated: false)
if checked {
tableView.selectRow(at: indexPath, animated: true, scrollPosition: .none)
}
// CULPRIT
cell.selectionStyle = .none
return cell
}
cell = UITableViewCell()
return cell
}
When I set the selection style on the storyboard (and removing the code equivalent), the problem went away!
Past it in your UITableViewCell class.
override func layoutSubviews() {
super.layoutSubviews()
subviews.forEach { (view) in
if type(of: view).description() == "_UITableViewCellSeparatorView" {
view.alpha = 1.0
}
}
}
This still seems to be a problem as of iOS 7.0.3, but I've worked around it with an unsophisticated means of faking the separator.
By first setting the UITableView's separator style to UITableViewCellSeparatorStyleNone. You can then use a custom UITableViewCell subclass to fake the separator between cells for both selected and unselected states:
#implementation MyTableViewCellSubclass
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
CGRect frame = self.bounds;
frame.origin.y = frame.size.height - 1.f;
frame.size.height = 1.f;
// Selected background view
//
UIView * separatorView = [[UIView alloc] initWithFrame:frame];
separatorView.backgroundColor = [UIColor darkGrayColor];
separatorView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleTopMargin;
UIView * selectedView = [[UIView alloc] initWithFrame:self.bounds];
selectedView.backgroundColor = [UIColor lightGrayColor];
[selectedView addSubview:separatorView];
self.selectedBackgroundView = selectedView;
// Add separator view to content view for unselected state
//
UIView * separatorView2 = [[UIView alloc] initWithFrame:frame];
separatorView2.backgroundColor = [UIColor darkGrayColor];
separatorView2.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleTopMargin;
[self.contentView addSubview:separatorView2];
}
return self;
}
#end
This simple call did it for me on iOS 8.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// ....
[tableView deselectRowAtIndexPath:indexPath animated:YES]
// ....
}
This'll just happen if you let iOS apply its own default selected cell style. Best work around I found so far is to override the selected property implementation:
in your cell subclass implementation:
#synthesize selected = _selected;
in the initialization method:
// problem actually is caused when you set following
// to UITableViewCellSelectionStyleDefault, so:
[self setSelectionStyle:UITableViewCellSelectionStyleNone];
overriding methods:
- (BOOL)selected
{
return _selected;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
_selected = selected
if (selected) {
// apply your own selected style
}
else {
// apply your own deselected style
}
}
I resolved this issue (hackishly) by reloading not just the selected cell but by also reloading the one right above it. None of the other solutions above worked for me.
NSIndexPath *indexPathOfCellAbove = [NSIndexPath indexPathForRow:(indexPath.row - 1) inSection:indexPath.section];
if (indexPath.row > 0)
[self.tableView reloadRowsAtIndexPaths:#[indexPathOfCellAbove, indexPath] withRowAnimation:UITableViewRowAnimationNone];
else
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
- cellForRowAtIndexPath
Create two separator views (sv1, sv2)
[cell addsubview:sv1];
[cell.selectedBackgroundView addsubview:sv2];
- didSelectRowAtIndexPath
[tableView deselectRowAtIndexPath:indexPath animated:NO];
In iOS 14, Apple has FINALLY made this less painful.
If you want to...
...be able to select rows (for example in edit mode)
...prevent the default gray or blue cell highlight color
...keep the default system separator views
...this will help you. In your UITableViewCell subclass, put this into the initializer:
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
// Prevent cell highlighting while preserving selectability and separator views
if #available(iOS 14.0, *) {
var backgroundConfig = UIBackgroundConfiguration.listPlainCell()
backgroundConfig.backgroundColor = .clear
backgroundConfiguration = backgroundConfig
} else {
selectedBackgroundView = {
let bgView = UIView(frame: .zero)
bgView.translatesAutoresizingMaskIntoConstraints = false
bgView.backgroundColor = .clear
return bgView
}()
}
}
If you're only targeting iOS 14+ you can leave out the else block and you're done. If you are also targeting iOS 13 and below, you'll also need to override layoutSubviews to keep the separator view from disappearing (thanks to this comment: https://stackoverflow.com/a/47573308/171933). This will do the trick (also in your UITableViewCell subclass):
override func layoutSubviews() {
super.layoutSubviews()
if #available(iOS 14.0, *) {
// no op
} else {
// Setting a custom selectedBackgroundView causes the system to hide the
// separatorView. If we want to have the separator, we need to show it again.
subviews.forEach { view in
if type(of: view).description() == "_UITableViewCellSeparatorView" {
view.alpha = 1.0
}
}
}
}
Enjoy.
For me it happened when I set programmatically:
cell.selectionStyle = UITableViewCellSelectionStyleNone;
When i set this property in the storyboard it works fine.
You could also trying setting the separator insets to 0. I did that and it solved the problem, but the trade-off is you lose the nice look of the insets.
This problem exists for single cell selection as well.
Another solution is to reload the table view, select followed by deselect:
self.selectedIndex = inIndexPath.row;
[inTableView reloadData];
[inTableView selectRowAtIndexPath:inIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
[inTableView deselectRowAtIndexPath:inIndexPath animated:YES];
This gets rid of a subtle graphical selection glitch I saw in Mark's solution.
this solution will not help anybody who isn't using a backgroundView on his cells, anyway:
- (void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
[cell setBackgroundColor:[UIColor grayColor]];
}
this way the annoying visual effect is vastly reduced without having to reload the table.
of course, you can change grayColor with anything which helps you improve the result in your case
use this:
- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = (UITableViewCell *)[tableView cellForRowAtIndexPath:indexPath];
UIView *selectionColor = [[UIView alloc] init];
selectionColor.backgroundColor = [UIColor clearColor];
cell.selectedBackgroundView = selectionColor;
//71
UIView* separatorLineView = [[UIView alloc] initWithFrame:CGRectMake(0, 71, 320, 2)];/// change size as you need, where - 71 - y coordinate, 320 - weight, 2 - height
// separatorLineView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"divider_goriz.png"]];// you can also put image here
separatorLineView.backgroundColor = [UIColor redColor];
[cell.selectedBackgroundView addSubview:separatorLineView];
return YES;
}
What I did was this:
Add a new subview under the content view of the cell.
Connect that from the cell as the selectedBackgroundView.
Add a subview of the new selected background view. Set it to start 16px from the left and cover the rest of the width, be 1px high, 1px down from the top and have a background color of 90% white.
In my case, I didn't want my rows shaded at all when selected, so I left the selected background view clear, but you can make it whatever color you like.
Also, I am not using autolayout, so just set my sizes appropriately. I presume with autolayout you would have to set up appropriate constraints.
For me, this completely resolved the problem (though I agree that this really does seem to be a bug in ios 7).
Too exciting, I solved this problem.
Add the following method call in a custom cell, and to set the color separator and frame. I'll hide the cell separator, and then customize the view on a load separator in superview. The impact separator cell is selected when this problem is solved friends
#interface MyCustomTableViewCell(){
UIView *customSeparatorView;
CGFloat separatorHight;
}
#property (nonatomic,weak)UIView *originSeparatorView;
#end
-(void)setSeparatorWithInset:(UIEdgeInsets)insets{
if (customSeparatorView) {
customSeparatorView.frame = CGRectMake(insets.left, insets.top,self.width - insets.left - insets.right, self.originSeparatorView.height-insets.bottom - insets.top);
self.originSeparatorView.hidden = YES;
self.originSeparatorView.alpha = 0;
}else{
for (int i = ([self.contentView.superview.subviews count] - 1); i >= 0; i--) {
UIView *subView = self.contentView.superview.subviews[i];
if ([NSStringFromClass(subView.class) hasSuffix:#"SeparatorView"]) {
self.originSeparatorView = subView;
subView.hidden = YES;
subView.alpha = 0;
subView.frame = CGRectMake(insets.left, insets.top,self.width - insets.left - insets.right, subView.height-insets.bottom - insets.top);
customSeparatorView = [[subView superview] viewWithTag:separatorViewTag];
if (!customSeparatorView) {
customSeparatorView = [[UIView alloc] initWithFrame:subView.frame];
customSeparatorView.tag = separatorViewTag;
[[subView superview] addSubview:customSeparatorView];
customSeparatorView.backgroundColor = [subView backgroundColor];
}
[[subView superview] bringSubviewToFront:customSeparatorView];
break;
}
}
}
}
-(void)setSeparatorColorWithColor:(UIColor *)sepColor{
if (customSeparatorView) {
customSeparatorView.backgroundColor = sepColor;
self.originSeparatorView.hidden = YES;
self.originSeparatorView.alpha = 0;
}else {
for (int i = ([self.contentView.superview.subviews count] - 1); i >= 0; i--) {
UIView *subView = self.contentView.superview.subviews[i];
if ([NSStringFromClass(subView.class) hasSuffix:#"SeparatorView"]) {
self.originSeparatorView = subView;
if (sepColor) {
subView.hidden = YES;
subView.alpha = 0;
subView.backgroundColor = sepColor;
customSeparatorView = [[subView superview] viewWithTag:separatorViewTag];
if (!customSeparatorView) {
customSeparatorView = [[UIView alloc] initWithFrame:subView.frame];
customSeparatorView.tag = separatorViewTag;
[[subView superview] addSubview:customSeparatorView];
customSeparatorView.backgroundColor = [subView backgroundColor];
}
[[subView superview] bringSubviewToFront:customSeparatorView];
}
break;
}
}
}
}
-(void)layoutSubviews{
[super layoutSubviews];
[self setSeparatorWithInset:UIEdgeInsetsMake(0, 0, 0, 0)];
[self setSeparatorColorWithColor:[UIColor colorWithRed:31/255.0 green:32/255.0f blue:35/255.0 alpha:0.2]];
}
what solved the issue for me was reloading the data after beginUpdates and endUpdates:
private func animateCellHeighChangeForTableView(tableView: UITableView, withDuration duration: Double) {
UIView.animateWithDuration(duration) { () -> Void in
tableView.beginUpdates();
tableView.endUpdates();
tableView.reloadData();
}
}
I needed the following:
"When user selects row, selection background color is
transparent/white/whatever you may call it and separator lines don't
disappear"
I've looked as well for a solution for the following problem:
"When I select a row in a table (plain type table) I had selection
colour grey, and if I set cell.selectionStyle to none -> Separators
between cells disappeared."
Xcode - 9.2 version
Found the following solution:
in 'tableView (....cellForRowAT...)'
let colorView = UIView(frame: CGRect(x: 0.0, y: 3.0, width:
cell.frame.width, height: cell.frame.height - 1.0))
colorView.backgroundColor = UIColor.white
UITableViewCellClass.appearance().selectedBackgroundView = colorView
UITableViewCellClass - is your prototype cell class
it makes possible to change selection color to white
in 'tableView (...didSelectRowAt)'
cell.selectionStyle = .none
in UITableViewCellClass (your prototype cell class)
override func layoutSubviews() {
super.layoutSubviews()
subviews.forEach { (view) in
if type(of: view).description() == "_UITableViewCellSeparatorView" {
view.alpha = 1.0
}
}
}
it allows to keep selected row with check mark and all separators are in place.
The solutions here didn't help me. In most cases it was proposed to remove the selection, but I wanted the cells to keep their selected state. So the idea is to disable the default separator line and use your own separator line. I tried this but I had problems with it (you can read more about this here). The main problem was drawing the line in the accessoryView area. It only worked on iOS 8, but I also needed a solution for iOS 7.
My requirements were:
Selection should be kept
Line should not disappear (especially in the case the cell get selected)
Separator line above the selected cell should also not disappear
Especially the third point made problems because iOS uses a kind of anti-aliasing effect for the crossing of on UITableViewCell to the next. As I found out that only occurs on iPad. It has the size of about one point in each direction (current selected cell, cell above) so that a line on the cell disappears even it is drawn on the cell itself (and not the default one used). It makes no difference if this line is on the cell above or on the selected cell. This special render effects hides my lines.
The solution looks like the following:
Use the backgroundView where you draw two lines: one on top (+1 point in y-direction for iPad and 0 point in y-direction for iPhone) and one on the bottom. So it never gets covered by the selection effect.
The created background view should only be used for the selected state (cell.selectedBackgroundView = selectedBackground). The default separator line is enabled for the other cells.
I have a working example with C# code posted here though you have to adapt it to your needs. Now my selection problems are gone!
I encountered this problem with IOS 13 and solved it in this way:
In tableView cell storyboard or xib file choose Selection NONE
In swift file of the cell override func:
override func setHighlighted(_ highlighted: Bool, animated: Bool) {
super.setHighlighted(highlighted, animated: animated)
if highlighted {
contentView.backgroundColor = .lightGray
} else {
contentView.backgroundColor = .clear
}
}
I wanted to get effect of regular selection like in previous IOS versions but if you want to get something else then customize the function with your colors.
For those of you looking for a solution in Swift, this fixed the issue for me. In your cellForRowAtIndexPath method, after you call dequeueReusableCellWithIdentifier, you just need to set the cells selectionStyle to .None
Here's the code:
override func tableView(tableView: UITableView?, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:TextTableViewCell = tableView!.dequeueReusableCellWithIdentifier("textCell", forIndexPath: indexPath) as! TextTableViewCell
cell.selectionStyle = .None // This fixes the disappearing border line issue
Related
I have a UITableView with custom tableview cells with the separator line between each cell. I recently began implementing multi cell selection when in editing mode. In order to have the blue circled checkmarks when each cell is selected, I changed cell selectionStyle from UITableViewCellSelectionStyleNone to UITableViewCellSelectionStyleDefault. To get rid of the gray shade when the cell is selected I just implemented a white background like this:
UIView * cellBackgroundView = [[UIView alloc] initWithFrame:cell.frame];
cellBackgroundView.backgroundColor = [UIColor clearColor];
cell.multipleSelectionBackgroundView = cellBackgroundView;
cell.selectedBackgroundView = cellBackgroundView;
The problem is that when I'm not in edit mode, the separator line disappears whenever the cell is selected, but when I select a cell in edit mode, the line remains. Any idea how to resolve this so the separator always remains?
try following:
Add this lines
[tableView deselectRowAtIndexPath:indexPath animated:YES];
[tableView reloadRowsAtIndexPaths:#[indexPath]
withRowAnimation:UITableViewRowAnimationAutomatic];
at the begining of didSelectRowAtIndexPath
this is weird bug which exists since iOS7.0
I know that the cell separator disappearing is a common issue, but none of the current solutions seemed to solve my problem so I came to the conclusion that I would need to toggle my selectionStyle to UITableViewCellSelectionStyleNone when my tableView is not in editing mode and UITableViewCellSelectionStyleBluewhen I'm in editing mode. I was able to achieve this by calling the following in my view controller containing my table view:
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if(tableView.editing)
{
[((customTableViewCellClass*)[tableView cellForRowAtIndexPath:indexPath]) changeSelectionStyle:YES];
}
else
{
[((customTableViewCellClass*)[tableView cellForRowAtIndexPath:indexPath]) changeSelectionStyle:NO];
}
return indexPath;
}
Then in my custom tableviewcell class I call the following:
-(void) changeSelectionStyle: (BOOL) selected
{
if(selected)
{
self.selectionStyle = UITableViewCellSelectionStyleBlue;
}
else
{
self.selectionStyle = UITableViewCellSelectionStyleNone;
}
}
In cellForRowAtIndexPath I left the code that changed the view for multipleSelectionBackgroundView
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UIView * cellBackgroundView = [[UIView alloc] initWithFrame:cell.frame];
cellBackgroundView.backgroundColor = [UIColor clearColor];
cell.multipleSelectionBackgroundView = cellBackgroundView;
cell.selectedBackgroundView = cellBackgroundView;
}
First of all, I am new at this and I am most likely forgetting something very simple.
Question:
I am making an application that displays random images from imgur.com in a tableView. For some reason all of the cells are indented a small amount as seen in the picture below. I have fiddled around with many settings in storyboard and have had no luck in fixing the issue.
Here is the tableView code...
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return (_images.count * 2);
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = nil;
if (indexPath.row % 2 == 0) {
//content cell
cell = [tableView dequeueReusableCellWithIdentifier:#"RandomImgurTableCell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"RandomImgurTableCell"];
}
long row = [indexPath row] / 2;
SDImageCache* myCache = [SDImageCache sharedImageCache];
cell.imageView.image = [myCache imageFromDiskCacheForKey:_images[row]];
}
else if (indexPath.row % 2 == 1) {
//separator cell
cell = [tableView dequeueReusableCellWithIdentifier:#"SeparatorCell"];
if(cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"SeparatorCell"];
}
}
if (indexPath.row == (_images.count * 2) - 3) {
[self checkToLoadMoreImages];
NSLog(#"Hit bottom of table, getting more images");
}
return cell;
}
Here is a picture of my tableView and cell settings...
In order to get rid of the indent, you set the separator insets to zero. The result is shown in the following image.
In Code
myTableView.separatorInset = UIEdgeInsetsZero
In the Interface Builder
Notes
If the cells are causing the indent, then you can add the following lines to tableView:cellForRowAtIndexPath:
cell.separatorInset = UIEdgeInsetsZero
cell.preservesSuperviewLayoutMargins = false
cell.layoutMargins = UIEdgeInsetsZero
If you are still supporting pre iOS 8 then see this answer for more details.
See also this Q&A.
Adjusting the separator inset should fix this. I believe that the default is 15px in from the left.
If you're referring to the horizontal indentation then you should try the following:
Make sure that the content view frame x origin is at 0:
yourTableViewCell.contentView.frame = CGRectMake(0.0f, 0.0f, SOME_WIDTH, SOME_HEIGHT);
You should make sure that the image view you're adding to the UITableViewCell is aligned with it's parent left edge. If you're using autolayout than do it in the interface builder. Otherwise:
yourImageView.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleHeight;
See that when the image is scaled, it remains true to the view frame:
yourImageView.contentMode = UIViewContentModeScaleAspectFill;
Make sure that the cell has 0 indentation:
yourTableViewCell.indentationLevel = 0;
If none of this helps, set a breakpoint and examine the subviews of the cell using one of the following debugger command:
po [[UIWindow keyWindow] recursiveDescription]
po [yourTableViewCell recursiveDescription]
None of the above worked for me, I found this instead:
https://www.hackingwithswift.com/example-code/uikit/how-to-make-uitableviewcell-separators-go-edge-to-edge
You need to do two things. First, add these two lines of code to your
table view controller's viewDidLoad() method:
tableView.layoutMargins = UIEdgeInsets.zero
tableView.separatorInset = UIEdgeInsets.zero
Now look for your cellForRowAt method and add this:
cell.layoutMargins = UIEdgeInsets.zero
I have a classic UITableViewController in an iOS 6.1 application. The table view is static and set in storyboard and it meant to represent configurable settings using UITableViewCellAccessoryCheckmark as ON/OFF switch. There is a section in TableView that disables another cell based on other setting. The cell is disabled with the following code:
cell.userInteractionEnabled = NO;
cell.textLabel.backgroundColor = [UIColor clearColor];
cell.alpha = 0.4;
The code is working fine once the TableView is loaded.
The problem is when TableView is loaded, the disabled cell code is not working correctly, because alpha setting in code does not work. UserInteractionEnabled property is working, but not the alpha setting.
This is code that should disable UITableViewCell when the view is loaded.
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell* cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
if (cell)
{
...
if ([[self currentSettingForIndexPath:indexPath] isEqualToString:#"myDependencySetting"])
{
...
cell.userInteractionEnabled = NO;
cell.textLabel.backgroundColor = [UIColor clearColor];
cell.alpha = 0.4;
NSLog(#"Disabling cell: %0.2f", cell.alpha);
...
}
else
{
cell.alpha = 1.0;
cell.userInteractionEnabled = YES;
}
}
NSLog(#"Cell alpha: %0.2f", cell.alpha);
return cell;
}
The code here is fairly simple, I ask the super for cell (since self is subclass of UITableViewController, it shouldn't be a problem), then configure the cell. If cell on current index path depends on another setting, I disable the cell (I stripped some other lines of code that are irrelevant).
The output reads:
...
Cell alpha: 1.00
...
Cell alpha: 1.00
Disabling cell: 0.40
Cell alpha: 0.40
Cell alpha 1.00
...
So according to output the alpha property of the cell is set correctly. But the cell alpha is not set, however the cell is not tappable because of userInteractionEnabled.
The same code works for disabling the cell in tableView's delegate didSelectRowAtIndex.
The question is: Why and what could override the cell's alpha value?
Thank you.
So I know it's two years down the road, but I grappled a bit with the same exact problem as you and stumbled across this question. I happen to be using iOS 8, but the problem seems the same: I've got a static table view with a couple of cells whose alpha I'd like to adjust on initial table view setup. Making these adjustments in cellForRowAtIndexPath did not work. Each cell adjusted showed on screen with an alpha of 1.0, although I was logging their alphas at 0 in cellForRowAtIndexPath.
Since I was interested in adjusting all cells of a single section, I was able to obtain the desired functionality by including something along these lines in viewWillAppear:
- (void)changeAlphaOfCells:(float)alpha
{
for (int i = 0; i < NUMBER_OF_ROWS_IN_SECTION; i++)
{
UITableViewCell *cell = [[self tableView] cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:SECTION_NUMBER]];
cell.alpha = alpha;
}
}
You'll need logic to determine which cells to adjust the alpha of in viewWillAppear, and retrieve them with cellForRowAtIndexPath. Not as elegant as a single line call in cellForRowAtIndexPath, but not too bad.
I think you cant access the cell's alpha directly like that. Try this
EDIT:
UIColor *colorWithAlphaOfPointFour = [UIColor colorWithRed:0 green:0 blue:0 alpha:.4];
[[cell contentView] setBackgroundColor:[UIColor colorWithAlphaOfPointFour]];
[[cell backgroundView] setBackgroundColor:[UIColor colorWithAlphaOfPointFour]];
[cell setBackgroundColor:[UIColor colorWithAlphaOfPointFour]];
Hi I want to use UITableHeaderFooterView in my app and i am doing this:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
[_tableView registerClass:[M3CTableViewCell class] forCellReuseIdentifier:#"cell"];
[_tableView registerClass:[M3CHeaderFooter class] forHeaderFooterViewReuseIdentifier:#"footer"];
}
- (UITableViewHeaderFooterView *)footerViewForSection:(NSInteger)section {
M3CHeaderFooter * footer = [[M3CHeaderFooter alloc]initWithReuseIdentifier:#"footer"];
footer.textLabel.text = #"Test";
return footer;
}
By doing this I am not getting anything at Footer's place.
And this method is not even getting called but I think this method is part of UITableViewDelegate protocol.
Using the new iOS 6 feature of reusable header/footer views involves two steps. You seem to be doing only the first step.
First step: you're telling the table view what class to use for the section header view, by registering your custom subclass of UITableViewHeaderFooterView (I assume your M3CHeaderFooter is a subclass of UITableViewHeaderFooterView).
Second step: Tell the table view what view to use (AND reuse) for a header section by implementing the tableView delegate method
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
So in your viewDidLoad you'd implement something like this:
// ****** Do Step One ******
[_tableView registerClass:[M3CHeaderFooter class] forHeaderFooterViewReuseIdentifier:#"TableViewSectionHeaderViewIdentifier"];
Then you'd implement the table View delegate method in the class where you're creating and displaying your table view:
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 40.0;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
static NSString *headerReuseIdentifier = #"TableViewSectionHeaderViewIdentifier";
// ****** Do Step Two *********
M3CHeaderFooter *sectionHeaderView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:headerReuseIdentifier];
// Display specific header title
sectionHeaderView.textLabel.text = #"specific title";
return sectionHeaderView;
}
Now mind you that you do not need to subclass UITableViewHeaderFooterView in order to use it.
Before iOS 6, if you wanted to have a header view for a section, you'd implement the above tableView delegate method and tell the table view what view to use for each section. So each section had a different instance of a UIView which you provided. This means that if your tableView had 100 sections, and inside the delegate method you created a new instance of a UIView, then you would have given the tableView 100 UIViews for the 100 section headers that were displayed.
Using the new feature of reusable header/footer views, you create an instance of a UITableViewHeaderFooterView and the system reuses it for each displayed section header.
If you wanted to have a reusable UITableViewHeaderFooterView without subclassing then you simply change your viewDidLoad to this:
// Register the class for a header view reuse.
[_buttomTableView registerClass:[UITableViewHeaderFooterView class] forHeaderFooterViewReuseIdentifier:#"TableViewSectionHeaderViewIdentifier"];
and then your delegate method to this:
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 40.0;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
static NSString *headerReuseIdentifier = #"TableViewSectionHeaderViewIdentifier";
// Reuse the instance that was created in viewDidLoad, or make a new one if not enough.
UITableViewHeaderFooterView *sectionHeaderView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:headerReuseIdentifier];
sectionHeaderView.textLabel.text = #"Non subclassed header";
return sectionHeaderView;
}
I hope that was clear enough.
EDIT: When subclassing the header view, you can implement code similar to the following if you wish to add a custom view to the headerView:
// Add any optional custom views of your own
UIView *customView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 50.0, 30.0)];
[customView setBackgroundColor:[UIColor blueColor]];
[sectionHeaderView.contentView addSubview:customView];
Doing this in the subclass, as opposed to viewForHeaderInSection: delegate method (as noted below by Matthias), will ensure that only one instance of any subviews are created. You can then add any properties within the subclass that will allow you to access your custom subview.
UITableViewHeaderFooterView is one of the few places I would programmatically handle the view rather than use Storyboard or a XIB. Since you cannot officially use appearance proxy and there is no IB way to do it without abusing UITableViewCells. I do it the old-fashioned way and just use the tag on the label to fetch the custom elements.
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UITableViewHeaderFooterView *headerView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:kSectionHeaderReuseIdentifier];
if (headerView == nil) {
[tableView registerClass:[UITableViewHeaderFooterView class] forHeaderFooterViewReuseIdentifier:kSectionHeaderReuseIdentifier];
headerView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:kSectionHeaderReuseIdentifier];
}
UILabel *titleLabel = (UILabel *)[headerView.contentView viewWithTag:1];
if (titleLabel == nil) {
UIColor *backgroundColor = [UIColor blackColor];
headerView.contentView.backgroundColor = backgroundColor;
titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(10.0f, 0.0f, 300.0f, 44.0f)];
titleLabel.textColor = [UIColor whiteColor];
titleLabel.backgroundColor = backgroundColor;
titleLabel.shadowOffset = CGSizeMake(0.0f, 0.0f);
titleLabel.tag = 1;
titleLabel.font = [UIFont systemFontOfSize:24.0f];
[headerView.contentView addSubview:titleLabel];
}
NSString *sectionTitle = [self.sections objectAtIndex:section];
if (sectionTitle == nil) {
sectionTitle = #"Missing Title";
}
titleLabel.text = sectionTitle;
return headerView;
}
This is an old post and has good answers, but I wanted to share another work-around for a very similar issue I experienced.
At first, I used:
-(UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
With a custom prototype cell for my header view. Subclassing UITableViewCell as such
static NSString *cellIdentifier = #"CustomHeaderCell";
CustomHeaderCell * cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
However, when animating TableView cells above section headers (making them twice as tall) the header view would disappear. This, as pointed out, is because the implementation only supplied a view, not a re-usable view.
Instead of forgoing everything with the customized prototype cell, I implemented the UITableViewHeaderFooterWithIdentifier and set it as the prototyped cell's contentView, without subclassing UITableViewHeaderFooterWithIdentifier.
static NSString *customHeaderViewIdentifier = #"CustomHeaderView";
UITableViewHeaderFooterView *headerView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:customHeaderViewIdentifier];
headerView = (UITableViewHeaderFooterView *)cell.contentView;
I realize this creates two instances of the header view (at least I think it would..) however it does allow you to keep the benefits of a customized prototype cell without doing everything programatically.
Full code:
// viewDidLoad
[myTableView registerClass:[UITableViewHeaderFooterView class] forHeaderFooterViewReuseIdentifier:#"CustomHeaderView"];
// Implement your custom header
-(UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
static NSString *cellIdentifier = #"CustomHeaderCell";
CustomHeaderCell * cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
static NSString *customHeaderViewIdentifier = #"CustomHeaderView";
UITableViewHeaderFooterView *headerView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:customHeaderViewIdentifier];
// do your cell-specific code here
// eg. cell.myCustomLabel.text = #"my custom text"
headerView = (UITableViewHeaderFooterView *)cell.contentView;
return headerView;
}
There are a few ways of approaching this, but here is one a solution in Swift: the idea here is that we have a subclass of UITableViewHeaderFooterView called SNStockPickerTableHeaderView; it exposes a method called, configureTextLabel() that when called, sets the font and the color of the text label. We call this method only after the title has been set, that is from, willDisplayHeaderView, and the font gets correctly set.
The header view also supports a custom line separator to set it apart from the rest of the cells.
// MARK: UITableViewDelegate
func tableView(tableView:UITableView, willDisplayHeaderView view:UIView, forSection section:Int) {
if let headerView:SNStockPickerTableHeaderView = view as? SNStockPickerTableHeaderView {
headerView.configureTextLabel()
}
}
func tableView(tableView:UITableView, viewForHeaderInSection section:Int) -> UIView? {
var headerView:SNStockPickerTableHeaderView? = tableView.dequeueReusableHeaderFooterViewWithIdentifier(kSNStockPickerTableHeaderViewReuseIdentifier) as? SNStockPickerTableHeaderView
if (headerView == nil) {
// Here we get to customize the section, pass in background color, text
// color, line separator color, etc.
headerView = SNStockPickerTableHeaderView(backgroundColor:backgroundColor,
textColor:primaryTextColor,
lineSeparatorColor:primaryTextColor)
}
return headerView!
}
And here is the custom UITableViewHeaderFooterView:
import Foundation
import UIKit
private let kSNStockPickerTableHeaderViewLineSeparatorHeight:CGFloat = 0.5
private let kSNStockPickerTableHeaderViewTitleFont = UIFont(name:"HelveticaNeue-Light", size:12)
let kSNStockPickerTableHeaderViewReuseIdentifier:String = "stock_picker_table_view_header_reuse_identifier"
class SNStockPickerTableHeaderView: UITableViewHeaderFooterView {
private var lineSeparatorView:UIView?
private var textColor:UIColor?
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// We must implement this, since the designated init of the parent class
// calls this by default!
override init(frame:CGRect) {
super.init(frame:frame)
}
init(backgroundColor:UIColor, textColor:UIColor, lineSeparatorColor:UIColor) {
super.init(reuseIdentifier:kSNStockPickerTableHeaderViewReuseIdentifier)
contentView.backgroundColor = backgroundColor
self.textColor = textColor
addLineSeparator(textColor)
}
// MARK: Layout
override func layoutSubviews() {
super.layoutSubviews()
let lineSeparatorViewY = CGRectGetHeight(self.bounds) - kSNStockPickerTableHeaderViewLineSeparatorHeight
lineSeparatorView!.frame = CGRectMake(0,
lineSeparatorViewY,
CGRectGetWidth(self.bounds),
kSNStockPickerTableHeaderViewLineSeparatorHeight)
}
// MARK: Public API
func configureTextLabel() {
textLabel.textColor = textColor
textLabel.font = kSNStockPickerTableHeaderViewTitleFont
}
// MARK: Private
func addLineSeparator(lineSeparatorColor:UIColor) {
lineSeparatorView = UIView(frame:CGRectZero)
lineSeparatorView!.backgroundColor = lineSeparatorColor
contentView.addSubview(lineSeparatorView!)
}
}
Here is the result, see section header for, "Popular Stocks":
I can't comment under Cameron Lowell Palmer post but to answer Christopher King, there is a simple way to ensure the re-use without sub-classing UITableViewHeaderFooterView and yet still using custom subviews.
First, do NOT register the class for a header view reuse.
Then in tableView:viewForHeaderInSection: you simply have to create UITableViewHeaderFooterView when needed:
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
static NSString *kYourTableViewReusableHeaderIdentifier = #"ID";
UILabel *titleLabel = nil;
UITableViewHeaderFooterView *headerView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:kYourTableViewReusableHeaderIdentifier];
if (headerView == nil) {
headerView = [[UITableViewHeaderFooterView alloc] initWithReuseIdentifier:kYourTableViewReusableHeaderIdentifier];
titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(...)];
titleLabel.tag = 1;
// ... setup titleLabel
[headerView.contentView addSubview:titleLabel];
} else {
// headerView is REUSED
titleLabel = (UILabel *)[headerView.contentView viewWithTag:1];
}
NSString *sectionTitle = (...); // Fetch value for current section
if (sectionTitle == nil) {
sectionTitle = #"Missing Title";
}
titleLabel.text = sectionTitle;
return headerView;
}
Here is a "quick-and-dirty" way to get this going. It will make a small blue label in the header. I've confirmed that this renders OK in iOS 6 and iOS 7.
in your UITableViewDelegate:
-(void)viewDidLoad
{
...
[self.table registerClass:[UITableViewHeaderFooterView class] forHeaderFooterViewReuseIdentifier:#"Header"];
...
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 34.;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UITableViewHeaderFooterView *header = [tableView dequeueReusableHeaderFooterViewWithIdentifier:#"Header"];
UILabel *leftlabel = [[UILabel alloc] initWithFrame:CGRectMake(0., 0., 400., 34.)];
[leftlabel setBackgroundColor:[UIColor blueColor]];
[header.contentView addSubview:leftlabel];
return header;
}
In case it gets lost in the thorough answers above, the thing that people are likely missing (compared to the standard cellForRowAtIndexPath: method) is that you must register the class used for the section header.
[tableView registerClass:[UITableViewHeaderFooterView class] forHeaderFooterViewReuseIdentifier:#"SectionHeader"];
Try adding registerClass:forHeaderFooterViewReuseIdentifier: and see if it starts working.
One of the reasons that method may not be being called is the style of the table. Standard vs Grouped handles headers/footers differently. That may explain why it's not getting called.
Set delegate property of UITableView instance to reference to the controller that implements next methods:
Method that returns view of section footer:
Asks the delegate for a view object to display in the footer of the specified section of the table view.
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
Height of view in section footer:
Asks the delegate for the height to use for the footer of a particular section.
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
I've read and tried a few answers I have found on StackOverflow. I've also read and tried a few things from blogs, but nothing seems to accomplish what I am looking for.
I create a UIView and set it's background color to my desired UITableViewCell selection color (instead of the standard blue or gray selection colors). I add this UIView to my cell's selectedBackgroundView and this works fine, my cell changes to the desired color on user selection.
This method works great on Plain UITableViews; not so well on Grouped. On a grouped UITableView, the 1st and last cell do not conform to clip / mask bounds as demonstrated in the below screenshots.
I know there is no way to round just the top-left and top-right corners only.
I want to do this strictly by code, without images.
Question
Does anyone know of a nice little work around to change the selectedBackgroundView color of a UITableViewCell using only the UIView and not images AND to make the 1st and last cell conform to the rounded corner boundaries?
Example
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString * CellIdentifier = #"Cell";
WCSBadgedCell * cell = [[WCSBadgedCell alloc] initWithStyle:UITableViewCellStyleSubtitle andBadgeStyle:0 reuseIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[WCSBadgedCell alloc] initWithStyle:UITableViewCellStyleDefault andBadgeStyle:0 reuseIdentifier:CellIdentifier];
}
UIView *bgColorView = [[UIView alloc] init];
[bgColorView setBackgroundColor:DARKBROWN];
[bgColorView setClipsToBounds: YES];
[cell.layer setMasksToBounds:YES];
[cell setSelectedBackgroundView:bgColorView];
[cell.textLabel setText: #"Testing a Cell"];
return cell;
}
Screenshots
Solution
I Accepted CodaFis answer because he added a comment which pointed to a pretty nice (yet lengthy) solution. I had to do quite a bit of revamping, but in the end, I now have the selectedBackgroundView's I needed which round the corners on the 1st and last cells, thanks again!
Here is a n example of how I achieved this.
I assume that you are using a UITableViewCell subclass because of the complexity of your cell. This is how I've been doing it:
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
if ((self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]))
{
self.clipsToBounds = YES;
UIView* bgView = [[UIView alloc] init];
bgView.backgroundColor = [UIColor colorWithWhite:0.f alpha:0.25f];
self.selectedBackgroundView = bgView;
//other code
}
return self;
}
This produces a sort of dark grey overlay on the cell, not images required!
In your case, the exact color of your selected cell (thanks to the handy dandy Digital Color Meter) would be
[UIColor colorWithRed:106.0f/255.0f green:51.0f/255.0f blue:6.0f/255.0f alpha:1.0f];
and the white text would be
- (void)setSelected:(BOOL)sel animated:(BOOL)animated
{
[super setSelected:sel animated:animated];
if (sel)
{
self.textLabel.textColor = [UIColor whiteColor];
}
else
{
self.textLabel.textColor = [UIColor colorWithRed:(105.f/255.f) green:(50.f/255.f) blue:(6.f/255.f) alpha:1.f];
}
}
I had the same problem, and fixed it by overriding -(void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated in my UITableViewCell subclass and setting no selection style when creating the cell
//set no selection style in the cell
...
[self setSelectionStyle:UITableViewCellSelectionStyleNone];
...
//override setHighlighted to manually set the regular/highlighted background colors
-(void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated {
[super setHighlighted:highlighted animated:animated];
if(highlighted){
self.backgroundColor = [UIColor greenColor];
}
else {
self.backgroundColor = [UIColor redColor];
}
}