Related
I have a UITableView that displays some content. When the user scrolls upward, the cells below don't always load immediately. This creates an area of white space at the bottom of the table. I would like to display a spinner in the white space and have it disappear when the content is done loading, but I'm not sure how to go about doing this. What is a good way to implement something like this in swift?
I am new to coding and iOS, please forgive me if the question is vague or the answer is obvious.
Sample Screenshot :
I think it's helpful for you..
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let lastSectionIndex = tableView.numberOfSections - 1
let lastRowIndex = tableView.numberOfRows(inSection: lastSectionIndex) - 1
if indexPath.section == lastSectionIndex && indexPath.row == lastRowIndex {
// print("this is the last cell")
let spinner = UIActivityIndicatorView(activityIndicatorStyle: .red)
spinner.startAnimating()
spinner.frame = CGRect(x: CGFloat(0), y: CGFloat(0), width: tableView.bounds.width, height: CGFloat(44))
self.tableview.tableFooterView = spinner
self.tableview.tableFooterView?.isHidden = false
}
}
tableFooterView should be hide when data load.
when above function isn't work so you can prefer this link.
Set UIActivityIndicatorView as a UITableView's footerView in viewDidLoad.
self.indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
self.indicator.hidesWhenStopped = YES;
self.indicator.frame = CGRectMake(0, 0, self.tableView.bounds.size.width, 44);
self.tableView.tableFooterView = self.indicator;
When the tableview is about to display the last row of cells and you have more data to load, then load more data.
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == self.arrList.count-1 && self.hasMoreList == YES) {
[self loadData];
}
}
Just start animating indicator before loading and stop animating it after loading data.
- (void)loadData {
[self.indicator startAnimating];
// load data and set hasMoreData here ...
[self.indicator stopAnimating];
}
I added multiple buttons in my UITableViewCell wrapped with a toolbar, but all of them are not clickable, once I drag a button to the outside of the table view it's clickable already.
Here is the screenshot of my sample app and scene:
Button 1 is not clickable but button 2 is clickable, User Interaction Enabled has been ticked.
Okay found the issue already, was a mistake by my own, it's because I set the tableViewCell "User Interaction Enabled" to NO, cause I want to disable the Table View default row selection.
So I need to set each layers of view "User Interaction Enabled" to YES, then the button is now clickable, thanks for all the replies!
Create you button in viewForHeaderInSection, like this..
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView *sectionView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 30)];
UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
[btn addTarget:self action:#selector(btnClicked) forControlEvents:UIControlEventTouchUpInside];
[sectionView addSubview:btn];
//add other subviews to section viewww..
//return
return sectionView;
}
- (void) btnClicked
{
//do your thing here..
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
cell.button.tag = indexPath.row
cell.button.addTarget(self, action: "btnClicked:", forControlEvents: .TouchUpInside)
}
I am setting a footer view in the viewDidLoad method:
UIView *fView = [[UIView alloc] initWithFrame:CGRectMake(0, 718, 239, 50)];
fView.backgroundColor =[UIColor yellowColor];
self.table.tableFooterView = fView;
Unfortunately, the footer is not drawing in the specified (x,y) specified above, but it stick with the cells, so if the table view has 4 cells, the footer will be drawn in the 5th cell.
I even tried the protocol method, tableView:viewForFooterInSection
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section{
UIView *fView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 239, 50)];
fView.backgroundColor =[UIColor yellowColor];
return fView;
}
the problem is not resolved, I am sure tableFooterView property should fi the footer view at the bottom of the table view but I am not sure what I may be missing here? Thanx in advance.
Since your goal is to have a footer that stays fixed at the bottom of the screen, and not scroll with the table, then you can't use a table view footer. In fact, you can't even use a UITableViewController.
You must implement your view controller as a UIViewController. Then you add your own table view as a subview. You also add your footer as a subview of the view controller's view, not the table view. Make sure you size the table view so its bottom is at the top of the footer view.
You will need to make your view controller conform to the UITableViewDataSource and UITableViewDelegate protocols and hook everything up to replicate the functionality of UITableViewController.
A footer view will always be added to the bottom of content.
This means that a section footer will be added below the cells of a section, a table footer view to the bottom of all sections - regardless of the position you set in your view.
If you want to add a "static" content, you should consider adding a view outside of the table view (superview) - which isn't possible if you use UITableViewController - or you use [self.table addSubView:view] and adjust the position/transform to the table view's contentOffset property in the scrollViewDidScroll: delegate method (UITableView is a subclass of UIScrollView so you also get it's delegate calls) like in this code:
#implementation YourTableViewController {
__weak UIView *_staticView;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIView *staticView = [[UIView alloc] initWithFrame:CGRectMake(0, self.tableView.bounds.size.height-50, self.tableView.bounds.size.width, 50)];
staticView.backgroundColor = [UIColor redColor];
[self.tableView addSubview:staticView];
_staticView = staticView;
self.tableView.contentInset = UIEdgeInsetsMake(0, 0, 50, 0);
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
_staticView.transform = CGAffineTransformMakeTranslation(0, scrollView.contentOffset.y);
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
// this is needed to prevent cells from being displayed above our static view
[self.tableView bringSubviewToFront:_staticView];
}
...
Another way is to use UITableViewController in a storyboard, and embed it within a UIViewController as a container view. Then you can use auto layout to set the relationship between the footer and the container view which contains the UITableView
If your table view or table view controller is wrapped by a navigation controller consider using the navigation controller's UIToolbar. It will always stick to the bottom.
[self.navigationController setToolbarHidden:NO];
It looks like something similar to below works quite well:
import PlaygroundSupport
import UIKit
let testVC = UITableViewController(style: .grouped)
testVC.view.frame = CGRect(x: 0, y: 0, width: 400, height: 700)
testVC.view.backgroundColor = .white
class TableViewDataSourceDelegate : NSObject {
var rows = 2
}
extension TableViewDataSourceDelegate : UITableViewDataSource, UITableViewDelegate {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return rows
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
cell.backgroundColor = .red
return cell
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
let tableViewHeight = tableView.bounds.size.height
let varticalMargin: CGFloat
if #available(iOS 11.0, *) {
varticalMargin = tableView.directionalLayoutMargins.bottom + tableView.directionalLayoutMargins.top
} else {
varticalMargin = tableView.layoutMargins.bottom + tableView.layoutMargins.top
}
let verticalInset: CGFloat
if #available(iOS 11.0, *) {
verticalInset = tableView.adjustedContentInset.bottom + tableView.adjustedContentInset.top
} else {
verticalInset = tableView.contentInset.bottom + tableView.contentInset.top
}
let tableViewContentHeight = tableView.contentSize.height - varticalMargin
let height: CGFloat
if #available(iOS 11.0, *) {
let verticalSafeAreaInset = tableView.safeAreaInsets.bottom + tableView.safeAreaInsets.top
height = tableViewHeight - tableViewContentHeight - verticalInset - verticalSafeAreaInset
} else {
height = tableViewHeight - tableViewContentHeight - verticalInset
}
if (height < 0) {
return 0
} else {
return height
}
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let extraButtonSpace = UIView()
extraButtonSpace.backgroundColor = .clear
return extraButtonSpace
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row == 0 {
tableView.beginUpdates()
rows += 1
tableView.insertRows(at: [indexPath], with: .automatic)
tableView.endUpdates()
} else if indexPath.row == 1 {
tableView.beginUpdates()
rows -= 1
tableView.deleteRows(at: [indexPath], with: .automatic)
tableView.endUpdates()
} else {
tableView.beginUpdates()
tableView.endUpdates()
}
}
}
let controller = TableViewDataSourceDelegate()
testVC.tableView.delegate = controller
testVC.tableView.dataSource = controller
testVC.tableView.reloadData()
let extraButtonSpace = UIView(frame: CGRect(x: 0, y: 0, width: 400, height: 80))
extraButtonSpace.backgroundColor = .yellow
testVC.tableView.tableFooterView = extraButtonSpace
PlaygroundPage.current.liveView = testVC.view
I was able to get a label to be fixed to the bottom of my static UITableViewController. Not the perfect solution for all scenarios, but worked for my simple needs.
UIView* v = [[UIView alloc] initWithFrame:self.view.bounds];
CGFloat labelHeight = 30;
CGFloat padding = 5;
UILabel* l = [[UILabel alloc] initWithFrame:CGRectMake(0, v.frame.size.height - labelHeight - padding, self.view.frame.size.width, labelHeight)];
l.text = #"Hello World";
[v addSubview:l];
[self.tableView setBackgroundView:v];
If you want to make footer fixed at bottom, you should create custom footerView and change footer frame when tableView content size is changing:
-(void)changeCustomTableFooterYPositionWithTableFrame:(CGRect)tableFrame tableContentSize: (CGSize) tableContentSize {
CGFloat originalTableViewTopEdgeInset = self.tableView.contentInset.top;
CGFloat originalTableViewBottomEdgeInset = self.tableView.contentInset.bottom - self.tableFooterView.frame.size.height;
CGFloat footerViewYPositionByContentSize = tableContentSize.height;
CGFloat footerViewYPositionByTableSize = tableFrame.size.height - self.tableFooterView.frame.size.height - originalTableViewTopEdgeInset - originalTableViewBottomEdgeInset;
CGFloat tableFooterViewYPosition = MAX(footerViewYPositionByContentSize, footerViewYPositionByTableSize);
self.tableFooterView.frame = CGRectMake(self.tableFooterView.frame.origin.x, tableFooterViewYPosition, self.customTableFooterView.frame.size.width, self.customTableFooterView.frame.size.height);
}
To detect when contentSize was changed add observer to contentSize:
[self addObserver: self forKeyPath: #"tableView.contentSize" options: NSKeyValueObservingOptionNew + NSKeyValueObservingOptionOld context: ContentSizeContext];
Do not forget to change tableView.edgeInsets when insert footer:
self.tableView.contentInset = UIEdgeInsetsMake(self.tableView.contentInset.top, self.tableView.contentInset.left, self.tableView.contentInset.bottom + self.customTableFooterView.frame.size.height, self.tableView.contentInset.right);
You can see inherited class and example at the link below:
TableViewWithFooterAtBottom
You can use this to make the table look smaller according to how many rows do you have :
let tblView = UIView(frame: CGRectZero)
tableView.tableFooterView = tblView
tableView.tableFooterView!.hidden = true
tableView.backgroundColor = UIColor.clearColor()
Another alternative would be to just change the height for row at index path depending on for what number minimum rows you have that problem.
The following is the solution for this footer problem, when we do NOT want the footer to stick in the bottom all the time, AKA. it only sticks to the bottom when there are not enough rows to fill the screen, or when the user scrolls all the way down of the screen.
Add your self.footerView to your self.tableView as a subview on -viewDidLoad: or somewhere like that, then set the delegate for self.tableView, update the content inset of the tableview to self.tableView.contentInset = UIEdgeInsetsMake(0, 0, CGRectGetHeight(self.footerView), 0); and set up the following methods:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
[self updateFooterView];
}
- (void)updateFooterView
{
CGRect sectionFrame = [self.tableView rectForSection:0];
CGFloat bottomSpace = self.tableView.contentOffset.y + CGRectGetHeight(self.tableView.frame) - CGRectGetMaxY(sectionFrame);
CGFloat footerHeight = CGRectGetHeight(self.footerView.frame);
CGFloat transformY = self.tableView.contentOffset.y + footerHeight - MIN(bottomSpace,footerHeight);
CGRect footerFrame = self.footerView.frame;
footerFrame.origin.y = self.tableView.bounds.size.height - footerFrame.size.height + transformY;
self.footerView.frame = footerFrame;
}
Whenever you need to update the footer (i.e. after adding a new row), just call -updateFooterView and you should be good
Im not super proud of this solution, but it worked for me using only IB as of today. It will use the toolbar area of your UITableViewController, if that works for you.
Create a new temporary UIViewController
Drag a Toolbar into this UIViewController
Drag a UIView on this toolbar. I used the elements tree on the left for that, was easier. This will create a BarButtonItem you'll move on step 5.
Drag a BarButtonItem on your UITableViewController, this will create a Toolbar items section.
Drag the BarButtonItem created on step 3 into the Toolbar items section created on step 4.
Delete the UIViewController and edit the BarButtonItem as you wish.
I want to customize UITableView header for each section. So far, I've implemented
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
this UITabelViewDelegate method. What I want to do is to get current header for each section and just add UILabel as a subview.
So far, I'm not able to accomplish that. Because, I couldn't find anything to get default section header. First question,is there any way to get default section header?
If it's not possible, I need to create a container view which is a UIView but,this time I need to set default background color,shadow color etc. Because, if you look carefully into section's header, it's already customized.
How can I get these default values for each section header?
You can try this:
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, tableView.frame.size.width, 18)];
/* Create custom view to display section header... */
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, 5, tableView.frame.size.width, 18)];
[label setFont:[UIFont boldSystemFontOfSize:12]];
NSString *string =[list objectAtIndex:section];
/* Section header is in 0th index... */
[label setText:string];
[view addSubview:label];
[view setBackgroundColor:[UIColor colorWithRed:166/255.0 green:177/255.0 blue:186/255.0 alpha:1.0]]; //your background color...
return view;
}
The selected answer using tableView :viewForHeaderInSection: is correct.
Just to share a tip here.
If you are using storyboard/xib, then you could create another prototype cell and use it for your "section cell". The code to configure the header is similar to how you configure for row cells.
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
static NSString *HeaderCellIdentifier = #"Header";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:HeaderCellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:HeaderCellIdentifier];
}
// Configure the cell title etc
[self configureHeaderCell:cell inSection:section];
return cell;
}
Swift version of Lochana Tejas answer:
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = UIView(frame: CGRectMake(0, 0, tableView.frame.size.width, 18))
let label = UILabel(frame: CGRectMake(10, 5, tableView.frame.size.width, 18))
label.font = UIFont.systemFontOfSize(14)
label.text = list.objectAtIndex(indexPath.row) as! String
view.addSubview(label)
view.backgroundColor = UIColor.grayColor() // Set your background color
return view
}
If you use default header view you can only change the text on it with
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
For Swift:
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
If you want to customize the view you need to create a new one your self.
why not use UITableViewHeaderFooterView?
If headerInSection isn't show, can try this.
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 45;
}
This returns a height for the header of a given section.
Swift 3 version of lochana and estemendoza answers:
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = UIView(frame: CGRect(x:0, y:0, width:tableView.frame.size.width, height:18))
let label = UILabel(frame: CGRect(x:10, y:5, width:tableView.frame.size.width, height:18))
label.font = UIFont.systemFont(ofSize: 14)
label.text = "This is a test";
view.addSubview(label);
view.backgroundColor = UIColor.gray;
return view
}
Also, be advised that you ALSO have to implement:
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 100;
}
The other answers do a good job of recreating the default header view, but don't actually answer your main question:
is there any way to get default section header ?
There is a way - just implement tableView:willDisplayHeaderView:forSection: in your delegate. The default header view will be passed into the second parameter, and from there you can cast it to a UITableViewHeaderFooterView and then add/change subviews as you wish.
Obj-C
- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section
{
UITableViewHeaderFooterView *headerView = (UITableViewHeaderFooterView *)view;
// Do whatever with the header view... e.g.
// headerView.textLabel.textColor = [UIColor whiteColor]
}
Swift
override func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int)
{
let headerView = view as! UITableViewHeaderFooterView
// Do whatever with the header view... e.g.
// headerView.textLabel?.textColor = UIColor.white
}
Try this......
override func tableView(tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int)
{
// Background view is at index 0, content view at index 1
if let bgView = view.subviews[0] as? UIView
{
// do your stuff
}
view.layer.borderColor = UIColor.magentaColor().CGColor
view.layer.borderWidth = 1
}
This is the easiest solution possible. The following code can be used directly for creating a custom section header.
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
SectionHeaderTableViewCell *headerView = [tableView dequeueReusableCellWithIdentifier:#"sectionHeader"];
//For creating a drop menu of rows from the section
//==THIS IS JUST AN EXAMPLE. YOU CAN REMOVE THIS IF-ELSE.==
if (![self.sectionCollapsedArray[section] boolValue])
{
headerView.imageView.image = [UIImage imageNamed:#"up_icon"];
}
else
{
headerView.imageView.image = [UIImage imageNamed:#"drop_icon"];
}
//For button action inside the custom cell
headerView.dropButton.tag = section;
[headerView.dropButton addTarget:self action:#selector(sectionTapped:) forControlEvents:UIControlEventTouchUpInside];
//For removing long touch gestures.
for (UIGestureRecognizer *recognizer in headerView.contentView.gestureRecognizers)
{
[headerView.contentView removeGestureRecognizer:recognizer];
[headerView removeGestureRecognizer:recognizer];
}
return headerView.contentView;
}
NOTE: SectionHeaderTableViewCell is a custom UITableViewCell created in Storyboard.
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
//put your values, this is part of my code
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 30.0f)];
[view setBackgroundColor:[UIColor redColor]];
UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(20, 5, 150, 20)];
[lbl setFont:[UIFont systemFontOfSize:18]];
[lbl setTextColor:[UIColor blueColor]];
[view addSubview:lbl];
[lbl setText:[NSString stringWithFormat:#"Section: %ld",(long)section]];
return view;
}
Full 2019 example to copy and paste
First set "Grouped" on storyboard: it has to happen at init time, you can't really set it later, so it's easier to remember to do it on storyboard:
Next,
Must implement heightForHeaderInSection due to Apple bug.
func tableView(_ tableView: UITableView,
heightForHeaderInSection section: Int) -> CGFloat {
return CGFloat(70.0)
}
There is still an Apple bug - for ten years now - where it simply won't show the first header (i.e., index 0) if you don't have heightForHeaderInSection call.
So, tableView.sectionHeaderHeight = 70 simply doesn't work, it's broken.
Setting a frame achieves nothing:
In viewForHeaderInSection simply create a UIView().
It is pointless / achieves nothing if you UIView(frame ...) since iOS simply sets the size of the view as determined by the table.
So the first line of viewForHeaderInSection will be simply let view = UIView() and that is the view you return.
func tableView(_ tableView: UITableView,
viewForHeaderInSection section: Int) -> UIView? {
let view = UIView()
let l = UILabel()
view.addSubview(l)
l.bindEdgesToSuperview()
l.backgroundColor = .systemOrange
l.font = UIFont.systemFont(ofSize: 15)
l.textColor = .yourClientsFavoriteColor
switch section {
case 0:
l.text = "First section on screen"
case 1:
l.text = "Here's the second section"
default:
l.text = ""
}
return view
}
That's it - anything else is a time waste.
Another "fussy" Apple issue.
The convenience extension used above is:
extension UIView {
// incredibly useful:
func bindEdgesToSuperview() {
guard let s = superview else {
preconditionFailure("`superview` nil in bindEdgesToSuperview")
}
translatesAutoresizingMaskIntoConstraints = false
leadingAnchor.constraint(equalTo: s.leadingAnchor).isActive = true
trailingAnchor.constraint(equalTo: s.trailingAnchor).isActive = true
topAnchor.constraint(equalTo: s.topAnchor).isActive = true
bottomAnchor.constraint(equalTo: s.bottomAnchor).isActive = true
}
}
If I were you, I would make a method which returns an UIView given a NSString to contain. For example
+ (UIView *) sectionViewWithTitle:(NSString *)title;
In the implementation of this method create a UIView, add a UILabel to it with the properties you want to set, and of course set its title to the given one.
#samwize's solution in Swift (so upvote him!). Brilliant using same recycling mechanism also for header/footer sections:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let settingsHeaderSectionCell:SettingsHeaderSectionCell = self.dequeueReusableCell(withIdentifier: "SettingsHeaderSectionCell") as! SettingsHeaderSectionCell
return settingsHeaderSectionCell
}
- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section
{
if([view isKindOfClass:[UITableViewHeaderFooterView class]]){
UITableViewHeaderFooterView *headerView = view;
[[headerView textLabel] setTextColor:[UIColor colorWithHexString:#"666666"]];
[[headerView textLabel] setFont:[UIFont fontWithName:#"fontname" size:10]];
}
}
If you want to change the font of the textLabel in your section header you want to do it in willDisplayHeaderView. To set the text you can do it in viewForHeaderInSection or titleForHeaderInSection. Good luck!
Magically add Table View Header in swift
Recently I tried this.
I needed one and only one header in the whole UITableView.
Like I wanted a UIImageView on the top of the TableView. So I added a UIImageView on top of the UITableViewCell and automatically it was added as a tableViewHeader. Now I connect the ImageView to the ViewController and added the Image.
I was confused because I did something like this for the first time. So to clear my confusion open the xml format of the MainStoryBoard and found the Image View was added as a header.
It worked for me. Thanks xCode and swift.
call this delegate method
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
return #"Some Title";
}
this will give a chance to automatically add a default header with dynamic title .
You may use reusable and customizable header / footer .
https://github.com/sourov2008/UITableViewCustomHeaderFooterSection
swif 4.2
override func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
guard let header = view as? UITableViewHeaderFooterView else { return }
header.textLabel?.textAlignment = .center // for all sections
switch section {
case 1: //only section No.1
header.textLabel?.textColor = .black
case 3: //only section No.3
header.textLabel?.textColor = .red
default: //
header.textLabel?.textColor = .yellow
}
}
besides to titleForHeaderInSection, you can simply change view of header, footer.
check my comment here: Change UITable section backgroundColor without loosing section Title
If you just want to add title to the tableView header dont add a view. In swift 3.x the code goes like this:
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
var lblStr = ""
if section == 0 {
lblStr = "Some String 1"
}
else if section == 1{
lblStr = "Some String 2"
}
else{
lblStr = "Some String 3"
}
return lblStr
}
You may implement an array to fetch the title for the headers.
Going back to the original question (4 years later), rather than rebuilding your own section header, iOS can simply call you (with willDisplayHeaderView:forSection:) right after it's built the default one. For example, I wanted to add a graph button on right edge of section header:
- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section {
UITableViewHeaderFooterView * header = (UITableViewHeaderFooterView *) view;
if (header.contentView.subviews.count > 0) return; //in case of reuse
CGFloat rightEdge = CGRectGetMaxX(header.contentView.bounds);
UIButton * button = [[UIButton alloc] initWithFrame:CGRectMake(rightEdge - 44, 0, 44, CGRectGetMaxY(header.contentView.bounds))];
[button setBackgroundImage:[UIImage imageNamed:#"graphIcon"] forState:UIControlStateNormal];
[button addTarget:self action:#selector(graphButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
[view addSubview:button];
}
Use tableView: willDisplayHeaderView: to customize the view when it is about to be displayed.
This gives you the advantage of being able to take the view that was already created for the header view and extend it, instead of having to recreate the whole header view yourself.
Here is an example that colors the header section based on a BOOL and adds a detail text element to the header.
- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section
{
// view.tintColor = [UIColor colorWithWhite:0.825 alpha:1.0]; // gray
// view.tintColor = [UIColor colorWithRed:0.825 green:0.725 blue:0.725 alpha:1.0]; // reddish
// view.tintColor = [UIColor colorWithRed:0.925 green:0.725 blue:0.725 alpha:1.0]; // pink
// Conditionally tint the header view
BOOL isMyThingOnOrOff = [self isMyThingOnOrOff];
if (isMyThingOnOrOff) {
view.tintColor = [UIColor colorWithRed:0.725 green:0.925 blue:0.725 alpha:1.0];
} else {
view.tintColor = [UIColor colorWithRed:0.925 green:0.725 blue:0.725 alpha:1.0];
}
/* Add a detail text label (which has its own view to the section header… */
CGFloat xOrigin = 100; // arbitrary
CGFloat hInset = 20;
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(xOrigin + hInset, 5, tableView.frame.size.width - xOrigin - (hInset * 2), 22)];
label.textAlignment = NSTextAlignmentRight;
[label setFont:[UIFont fontWithName:#"Helvetica-Bold" size:14.0]
label.text = #"Hi. I'm the detail text";
[view addSubview:label];
}
Swift 4.2
In Swift 4.2 the name of table is a little changed.
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.size.width, height: 18))
let label = UILabel(frame: CGRect(x: 10, y: 5, width: tableView.frame.size.width, height: 18))
label.font = UIFont.systemFont(ofSize: 14)
label.text = list.objectAtIndex(section) as! String
view.addSubview(label)
view.backgroundColor = UIColor.gray // Set your background color
return view
}
Code for Swift 5
We can implement this by using two tableView delegate functions:
1] We can give custom height for the section:
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 49
}
2] Then we can create custom header:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let sectionV = UIView.init(frame: CGRect(x: 0, y: 0, width: tableView.frame.width, height: 48) )
let titleLbl = UILabel.init(frame: CGRect(x: 25, y: 24, width: tableView.frame.width-150, height: 20) )
let viewAllBtn = UIButton.init(frame: CGRect(x: tableView.frame.width-150, y: 15, width: self.view.frame.width - titleLbl.frame.width, height: 45))
viewAllBtn.titleLabel?.font = UIFont.systemFont(ofSize: 15)
viewAllBtn.setTitle("View All", for: .normal)
viewAllBtn.setTitleColor(.systemBlue, for: .normal)
viewAllBtn.tag = section
titleLbl.text = dashboardTempData.data?[section].title
titleLbl.font = UIFont.systemFont(ofSize: 21, weight: UIFont.Weight.medium)
sectionV.backgroundColor = .systemBackground
sectionV.addSubview(titleLbl)
sectionV.addSubview(viewAllBtn)
sectionV.bringSubviewToFront(viewAllBtn)
return sectionV
}
It will create a Label and Button with a section header height of 49
I'm customizing a UITableView. I want to hide the line separating on the last cell ... can i do this?
I know I can do tableView.separatorStyle = UITableViewCellStyle.None but that would affect all the cells of the tableView. I want it to only affect my last cell.
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);
}
In the UITableViewDataSource cellForRowAtIndexPath method
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.
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()
}
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.
Set separatorInset.right = .greatestFiniteMagnitude on your cell.
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, Swift 4 and Swift 5, you can write an extension to UITableViewCell like this:
extension UITableViewCell {
func separator(hide: Bool) {
separatorInset.left = hide ? bounds.size.width : 0
}
}
Then you can use this as below (when cell is your cell instance):
cell.separator(hide: false) // Shows separator
cell.separator(hide: true) // Hides separator
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.
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
}
}
}
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.
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.
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)
}
}
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
In willdisplaycell:
cell.separatorInset = UIEdgeInsetsMake(0, cell.bounds.size.width, 0, 0)
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 table view cell separator. And this approach removes only the last table view cell separator, and you don't need to think about Auto Layout issues (i.e. rotating device) or hardcode values to set up separator insets.
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
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
}
}
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!
}
For Swift 2:
add the following line to viewDidLoad():
tableView.separatorColor = UIColor.clearColor()
cell.separatorInset = UIEdgeInsetsMake(0.0, cell.bounds.size.width, 0.0, -cell.bounds.size.width)
works well in iOS 10.2
Swift 5 - iOS13+
When you are defininig your table, just add:
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
// Removes separator lines
tableView.separatorStyle = UITableViewCell.SeparatorStyle.none
return UIView()
}
The magic line is tableView.separatorStyle = UITableViewCell.SeparatorStyle.none
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 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.
if the accepted answer doesn't work, you can try this:
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
return 0.01f; }
It's great ;)
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);
}
}
Inside the tableview cell class. put these line of code
separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: self.bounds.size.width)