Hide separator line on one UITableViewCell

旧城冷巷雨未停 提交于 2019-11-26 11:58:49
Hiren

in viewDidLoad, add this line:

self.tableView.separatorColor = [UIColor clearColor];

and in cellForRowAtIndexPath:

for iOS lower versions

if(indexPath.row != self.newCarArray.count-1){
    UIImageView *line = [[UIImageView alloc] initWithFrame:CGRectMake(0, 44, 320, 2)];
    line.backgroundColor = [UIColor redColor];
    [cell addSubview:line];
}

for iOS 7 upper versions (including iOS 8)

if (indexPath.row == self.newCarArray.count-1) {
    cell.separatorInset = UIEdgeInsetsMake(0.f, cell.bounds.size.width, 0.f, 0.f);
}
Avinash

You can use the following code:

Swift :

if indexPath.row == {your row number} {
    cell.separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: .greatestFiniteMagnitude)
}

or :

cell.separatorInset = UIEdgeInsetsMake(0, 0, 0, UIScreen.main.bounds.width)

for default Margin:

cell.separatorInset = UIEdgeInsetsMake(0, tCell.layoutMargins.left, 0, 0)

to show separator end-to-end

cell.separatorInset = .zero

Objective-C:

if (indexPath.row == {your row number}) {
    cell.separatorInset = UIEdgeInsetsMake(0.0f, 0.0f, 0.0f, CGFLOAT_MAX);
}

To follow up on Hiren's answer.

in ViewDidLoad and the following line :

self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;

Or, if you are using XIB's or Storyboards change "separator" to "none" :

And in CellForRowAtIndexPath add this :

CGFloat separatorInset; // Separator x position 
CGFloat separatorHeight; 
CGFloat separatorWidth; 
CGFloat separatorY; 
UIImageView *separator;
UIColor *separatorBGColor;

separatorY      = cell.frame.size.height;
separatorHeight = (1.0 / [UIScreen mainScreen].scale);  // This assures you to have a 1px line height whatever the screen resolution
separatorWidth  = cell.frame.size.width;
separatorInset  = 15.0f;
separatorBGColor  = [UIColor colorWithRed: 204.0/255.0 green: 204.0/255.0 blue: 204.0/255.0 alpha:1.0];

separator = [[UIImageView alloc] initWithFrame:CGRectMake(separatorInset, separatorY, separatorWidth,separatorHeight)];
separator.backgroundColor = separatorBGColor;
[cell addSubView: separator];

Here is an example of the result where I display a tableview with dynamic Cells (but only have a single one with contents). The result being that only that one has a separator and not all the "dummy" ones tableview automatically adds to fill the screen.

Hope this helps.

EDIT: For those who don't always read the comments, there actually is a better way to do it with a few lines of code :

override func viewDidLoad() {
    super.viewDidLoad()
    tableView.tableFooterView = UIView()
}
Kemenaran

If you don't want to draw the separator yourself, use this:

  // Hide the cell separator by moving it to the far right
  cell.separatorInset = UIEdgeInsetsMake(0, 10000, 0, 0);

This API is only available starting from iOS 7 though.

my develop environment is

  • Xcode 7.0
  • 7A220 Swift 2.0
  • iOS 9.0

above answers not fully work for me

after try, my finally working solution is:

let indent_large_enought_to_hidden:CGFloat = 10000
cell.separatorInset = UIEdgeInsetsMake(0, indent_large_enought_to_hidden, 0, 0) // indent large engough for separator(including cell' content) to hidden separator
cell.indentationWidth = indent_large_enought_to_hidden * -1 // adjust the cell's content to show normally
cell.indentationLevel = 1 // must add this, otherwise default is 0, now actual indentation = indentationWidth * indentationLevel = 10000 * 1 = -10000

and the effect is:

In Swift 3 and Swift 4, you can write an extension to UITableViewCell like this:

extension UITableViewCell {

  func hideSeparator() {
    self.separatorInset = UIEdgeInsets(top: 0, left: self.bounds.size.width, bottom: 0, right: 0)
  }

  func showSeparator() {
    self.separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
  }
}

Then you can use this as below (when cell is your cell instance):

cell.hideSeparator()
cell.showSeparator()

It is really better assigning the width of table view cell as left inset instead of assigning it some random number. Because in some screen dimensions, maybe not now but in future your separators can still be visible because that random number may not be enough. Also, in iPad in landscape mode you can't guarantee that your separators will always be invisible.

Better solution for iOS 7 & 8

-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    DLog(@"");
    if (cell && indexPath.row == 0 && indexPath.section == 0) {

        DLog(@"cell.bounds.size.width %f", cell.bounds.size.width);
        cell.separatorInset = UIEdgeInsetsMake(0.f, cell.bounds.size.width, 0.f, 0.0f);
    }
}

If your app is rotatable — use 3000.0f for left inset constant or calc it on the fly. If you try to set right inset you have visible part of separator on the left side of cell on iOS 8.

Enrico Susatyo

In iOS 7, the UITableView grouped style cell separator looks a bit different. It looks a bit like this:

I tried Kemenaran's answer of doing this:

cell.separatorInset = UIEdgeInsetsMake(0, 10000, 0, 0);

However that doesn't seem to work for me. I'm not sure why. So I decided to use Hiren's answer, but using UIView instead of UIImageView, and draws the line in the iOS 7 style:

UIColor iOS7LineColor = [UIColor colorWithRed:0.82f green:0.82f blue:0.82f alpha:1.0f];

//First cell in a section
if (indexPath.row == 0) {

    UIView *line = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 1)];
    line.backgroundColor = iOS7LineColor;
    [cell addSubview:line];
    [cell bringSubviewToFront:line];

} else if (indexPath.row == [self.tableViewCellSubtitles count] - 1) {

    UIView *line = [[UIView alloc] initWithFrame:CGRectMake(21, 0, self.view.frame.size.width, 1)];
    line.backgroundColor = iOS7LineColor;
    [cell addSubview:line];
    [cell bringSubviewToFront:line];

    UIView *lineBottom = [[UIView alloc] initWithFrame:CGRectMake(0, 43, self.view.frame.size.width, 1)];
    lineBottom.backgroundColor = iOS7LineColor;
    [cell addSubview:lineBottom];
    [cell bringSubviewToFront:lineBottom];

} else {

    //Last cell in the table view
    UIView *line = [[UIView alloc] initWithFrame:CGRectMake(21, 0, self.view.frame.size.width, 1)];
    line.backgroundColor = iOS7LineColor;
    [cell addSubview:line];
    [cell bringSubviewToFront:line];
}

If you use this, make sure you plug in the correct table view height in the second if statement. I hope this is useful for someone.

Tualatrix Chou

In your UITableViewCell subclass, override layoutSubviews and hide the _UITableViewCellSeparatorView. Works under iOS 10.

override func layoutSubviews() {
    super.layoutSubviews()

    subviews.forEach { (view) in
        if view.dynamicType.description() == "_UITableViewCellSeparatorView" {
            view.hidden = true
        }
    }
}

Set separatorInset.right = .greatestFiniteMagnitude on your cell.

In Swift using iOS 8.4:

/*
    Tells the delegate that the table view is about to draw a cell for a particular row. (optional)
*/
override func tableView(tableView: UITableView,
                        willDisplayCell cell: UITableViewCell,
                        forRowAtIndexPath indexPath: NSIndexPath)
{
    if indexPath.row == 3 {
        // Hiding separator line for only one specific UITableViewCell
        cell.separatorInset = UIEdgeInsetsMake(0, cell.bounds.size.width, 0, 0)
    }
}

Note: this snippet above will work on UITableView using dynamic cells. The only problem that you can encounter is when you use static cells with categories, a separator type different than none and a grouped style for the table view. In fact, in this particular case it will not hide the last cell of each category. For overcoming that, the solution that I found was to set the cell separator (through IB) to none and then creating and adding manually (through code) your line view to each cell. For an example, please check the snippet below:

/*
Tells the delegate that the table view is about to draw a cell for a particular row. (optional)
*/
override func tableView(tableView: UITableView,
    willDisplayCell cell: UITableViewCell,
    forRowAtIndexPath indexPath: NSIndexPath)
{
    // Row 2 at Section 2
    if indexPath.row == 1 && indexPath.section == 1 {
        // Hiding separator line for one specific UITableViewCell
        cell.separatorInset = UIEdgeInsetsMake(0, cell.bounds.size.width, 0, 0)

        // Here we add a line at the bottom of the cell (e.g. here at the second row of the second section).
        let additionalSeparatorThickness = CGFloat(1)
        let additionalSeparator = UIView(frame: CGRectMake(0,
            cell.frame.size.height - additionalSeparatorThickness,
            cell.frame.size.width,
            additionalSeparatorThickness))
        additionalSeparator.backgroundColor = UIColor.redColor()
        cell.addSubview(additionalSeparator)
    }
}

Use this subclass, set separatorInset does not work for iOS 9.2.1, content would be squeezed.

@interface NSPZeroMarginCell : UITableViewCell

@property (nonatomic, assign) BOOL separatorHidden;

@end

@implementation NSPZeroMarginCell

- (void) layoutSubviews {
    [super layoutSubviews];

    for (UIView *view in  self.subviews) {
        if (![view isKindOfClass:[UIControl class]]) {
            if (CGRectGetHeight(view.frame) < 3) {
                view.hidden = self.separatorHidden;
            }
        }
    }
}

@end

https://gist.github.com/liruqi/9a5add4669e8d9cd3ee9

Greg Price

I do not believe this approach will work under any circumstance with dynamic cells...

if (indexPath.row == self.newCarArray.count-1) {
  cell.separatorInset = UIEdgeInsetsMake(0.f, cell.bounds.size.width, 0.f, 0.f);
}

It doesn't matter which tableview method you do it in for dynamic cells the cell you changed the inset property on will always have the inset property set now every time it is dequeued causing a rampage of missing line separators... That is until you change it yourself.

Something like this worked for me:

if indexPath.row == franchises.count - 1 {
  cell.separatorInset = UIEdgeInsetsMake(0, cell.contentView.bounds.width, 0, 0)
} else {
  cell.separatorInset = UIEdgeInsetsMake(0, 0, cell.contentView.bounds.width, 0)
}

That way you update ur data structure state at every load

Using Swift 3 and adopting the fastest hacking-method, you can improve code using extensions:

extension UITableViewCell {

    var isSeparatorHidden: Bool {
        get {
            return self.separatorInset.right != 0
        }
        set {
            if newValue {
                self.separatorInset = UIEdgeInsetsMake(0, self.bounds.size.width, 0, 0)
            } else {
                self.separatorInset = UIEdgeInsetsMake(0, 0, 0, 0)
            }
        }
    }

}

Then, when you configure cell:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "identifier", for: indexPath)
    switch indexPath.row {
       case 3:
          cell.isSeparatorHidden = true
       default:
          cell.isSeparatorHidden = false
    }
    return cell
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let cell = tableView.cellForRow(at: indexPath)
    if cell.isSeparatorHidden { 
       // do stuff
    }
}

The much more simple and logical is to do this:

- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section { return [[UIView alloc] initWithFrame:CGRectZero]; }

In most cases you don't want to see only the last tableCiewCell separator. And this approach removes only the last tableViewCell separator, and you don't need to think about autolayout issues (i.e. rotating device) or hardcode values to set up separator insets.

Kimate Richards

In willdisplaycell:

cell.separatorInset = UIEdgeInsetsMake(0, cell.bounds.size.width, 0, 0)
  if([_data count] == 0 ){
       [self.tableView setSeparatorStyle:UITableViewCellSeparatorStyleNone];//  [self tableView].=YES;
    } else {
      [self.tableView setSeparatorStyle:UITableViewCellSeparatorStyleSingleLine];////    [self tableView].hidden=NO;
    }

The best way to achieve this is to turn off default line separators, subclass UITableViewCell and add a custom line separator as a subview of the contentView - see below a custom cell that is used to present an object of type SNStock that has two string properties, ticker and name:

import UIKit

private let kSNStockCellCellHeight: CGFloat = 65.0
private let kSNStockCellCellLineSeparatorHorizontalPaddingRatio: CGFloat = 0.03
private let kSNStockCellCellLineSeparatorBackgroundColorAlpha: CGFloat = 0.3
private let kSNStockCellCellLineSeparatorHeight: CGFloat = 1

class SNStockCell: UITableViewCell {

  private let primaryTextColor: UIColor
  private let secondaryTextColor: UIColor

  private let customLineSeparatorView: UIView

  var showsCustomLineSeparator: Bool {
    get {
      return !customLineSeparatorView.hidden
    }
    set(showsCustomLineSeparator) {
      customLineSeparatorView.hidden = !showsCustomLineSeparator
    }
  }

  var customLineSeparatorColor: UIColor? {
   get {
     return customLineSeparatorView.backgroundColor
   }
   set(customLineSeparatorColor) {
     customLineSeparatorView.backgroundColor = customLineSeparatorColor?.colorWithAlphaComponent(kSNStockCellCellLineSeparatorBackgroundColorAlpha)
    }
  }

  required init(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }

  init(reuseIdentifier: String, primaryTextColor: UIColor, secondaryTextColor: UIColor) {
    self.primaryTextColor = primaryTextColor
    self.secondaryTextColor = secondaryTextColor
    self.customLineSeparatorView = UIView(frame:CGRectZero)
    super.init(style: UITableViewCellStyle.Subtitle, reuseIdentifier:reuseIdentifier)
    selectionStyle = UITableViewCellSelectionStyle.None
    backgroundColor = UIColor.clearColor()

    contentView.addSubview(customLineSeparatorView)
    customLineSeparatorView.hidden = true
  }

  override func prepareForReuse() {
    super.prepareForReuse()
    self.showsCustomLineSeparator = false
  }

  // MARK: Layout

  override func layoutSubviews() {
    super.layoutSubviews()
    layoutCustomLineSeparator()
  }

  private func layoutCustomLineSeparator() {
    let horizontalPadding: CGFloat = bounds.width * kSNStockCellCellLineSeparatorHorizontalPaddingRatio
    let lineSeparatorWidth: CGFloat = bounds.width - horizontalPadding * 2;
    customLineSeparatorView.frame = CGRectMake(horizontalPadding,
      kSNStockCellCellHeight - kSNStockCellCellLineSeparatorHeight,
      lineSeparatorWidth,
      kSNStockCellCellLineSeparatorHeight)
  }

  // MARK: Public Class API

  class func cellHeight() -> CGFloat {
    return kSNStockCellCellHeight
  }

  // MARK: Public API

  func configureWithStock(stock: SNStock) {
    textLabel!.text = stock.ticker as String
    textLabel!.textColor = primaryTextColor
    detailTextLabel!.text = stock.name as String
    detailTextLabel!.textColor = secondaryTextColor
    setNeedsLayout()
  } 
}

To disable the default line separator use, tableView.separatorStyle = UITableViewCellSeparatorStyle.None;. The consumer side is relatively simple, see example below:

private func stockCell(tableView: UITableView, indexPath:NSIndexPath) -> UITableViewCell {
  var cell : SNStockCell? = tableView.dequeueReusableCellWithIdentifier(stockCellReuseIdentifier) as? SNStockCell
  if (cell == nil) {
    cell = SNStockCell(reuseIdentifier:stockCellReuseIdentifier, primaryTextColor:primaryTextColor, secondaryTextColor:secondaryTextColor)
  }
  cell!.configureWithStock(stockAtIndexPath(indexPath))
  cell!.showsCustomLineSeparator = true
  cell!.customLineSeparatorColor = tintColor
  return cell!
}

if the accepted answer doesn't work, you can try this:

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
    return 0.01f; }

It's great ;)

Swift:

public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    ...

    // remove separator for last cell
    cell.separatorInset = indexPath.row < numberOfRowsInSection-1
        ? tableView.separatorInset
        : UIEdgeInsets(top: 0, left: tableView.bounds.size.width, bottom: 0, right: 0)

    return cell
}

Objective-C:

- (UITableViewCell *)tableView:(UITableView *)tableView
     cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    ...

    // remove separator for last cell
    cell.separatorInset = (indexPath.row < numberOfRowsInSection-1)
        ? tableView.separatorInset
        : UIEdgeInsetsMake(0.f, tableView.bounds.size.width, 0.f, 0.f);

    return cell;
}

Try the below code, might help you resolve your problem

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

   NSString* reuseIdentifier = @"Contact Cell";

    UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
    if (nil == cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
if (indexPath.row != 10) {//Specify the cell number
        cell.backgroundView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"bgWithLine.png"]];

} else {
        cell.backgroundView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"bgWithOutLine.png"]];

}

    }

    return cell;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

       NSString *cellId = @"cell";
       UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
       NSInteger lastRowIndexInSection = [tableView numberOfRowsInSection:indexPath.section] - 1;

       if (row == lastRowIndexInSection) {
              CGFloat halfWidthOfCell = cell.frame.size.width / 2;
              cell.separatorInset = UIEdgeInsetsMake(0, halfWidthOfCell, 0, halfWidthOfCell);
       }
}

You have to take custom cell and add Label and set constraint such as label should cover entire cell area. and write the below line in constructor.

- (void)awakeFromNib {
    // Initialization code
    self.separatorInset = UIEdgeInsetsMake(0, 10000, 0, 0);
    //self.layoutMargins = UIEdgeInsetsZero;
    [self setBackgroundColor:[UIColor clearColor]];
    [self setSelectionStyle:UITableViewCellSelectionStyleNone];
}

Also set UITableView Layout margin as follow

tblSignup.layoutMargins = UIEdgeInsetsZero;

I couldn't hide the separator on a specific cell except using the following workaround

- (void)layoutSubviews {
    [super layoutSubviews];
    [self hideCellSeparator];
}
// workaround
- (void)hideCellSeparator {
    for (UIView *view in  self.subviews) {
        if (![view isKindOfClass:[UIControl class]]) {
            [view removeFromSuperview];
        }
    }
}

For Swift 2:

add the following line to viewDidLoad():

tableView.separatorColor = UIColor.clearColor()

For iOS7 and above, the cleaner way is to use INFINITY instead of hardcoded value. You don't have to worry on updating the cell when the screen rotates.

if (indexPath.row == <row number>) {
    cell.separatorInset = UIEdgeInsetsMake(0, INFINITY, 0, 0);
}

As (many) others have pointed out, you can easily hide all UITableViewCell separators by simply turning them off for the entire UITableView itself; eg in your UITableViewController

- (void)viewDidLoad {
    ...
    self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
    ...
}

Unfortunately, its a real PITA to do on a per-cell basis, which is what you are really asking.

Personally, I've tried numerous permutations of changing the cell.separatorInset.left, again, as (many) others have suggested, but the problem is, to quote Apple (emphasis added):

"...You can use this property to add space between the current cell’s contents and the left and right edges of the table. Positive inset values move the cell content and cell separator inward and away from the table edges..."

So if you try to 'hide' the separator by shoving it offscreen to the right, you can end up also indenting your cell's contentView too. As suggested by crifan, you can then try to compensate for this nasty side-effect by setting cell.indentationWidth and cell.indentationLevel appropriately to move everything back, but I've found this to also be unreliable (content still getting indented...).

The most reliable way I've found is to over-ride layoutSubviews in a simple UITableViewCell subclass and set the right inset so that it hits the left inset, making the separator have 0 width and so invisible [this needs to be done in layoutSubviews to automatically handle rotations]. I also add a convenience method to my subclass to turn this on.

@interface MyTableViewCellSubclass()
@property BOOL separatorIsHidden;
@end

@implementation MyTableViewCellSubclass

- (void)hideSeparator
{
    _separatorIsHidden = YES;
}

- (void)layoutSubviews
{
    [super layoutSubviews];

    if (_separatorIsHidden) {
        UIEdgeInsets inset = self.separatorInset;
        inset.right = self.bounds.size.width - inset.left;
        self.separatorInset = inset;
    }
}

@end

Caveat: there isn't a reliable way to restore the original right inset, so you cant 'un-hide' the separator, hence why I'm using an irreversible hideSeparator method (vs exposing separatorIsHidden). Please note the separatorInset persists across reused cells so, because you can't 'un-hide', you need to keep these hidden-separator cells isolated in their own reuseIdentifier.

SMS

My requirement was to hide the separator between 4th and 5th cell. I achieved it by

    -(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    if(indexPath.row == 3)
    {
        cell.separatorInset = UIEdgeInsetsMake(0, cell.bounds.size.width, 0, 0);
    }
}
cell.separatorInset = UIEdgeInsetsMake(0.0, cell.bounds.size.width, 0.0, -cell.bounds.size.width)

works well in iOS 10.2

Tommy

If you are using StoryBoard, you can just simply select TableView and on "Seperator" change it to "none"

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!