I am trying to set height of the last tableView row different from the others.
Instead of creating a new cell etc, I want to "cut" 10px from the height of this cell. As I have set automatic dimension for height, I can't use a constant value.
This is how it looks like:
tableView.estimatedRowHeight = 54.0
return UITableViewAutomaticDimension
I want something like this, but this does not work:
if isLastLineBla {
tableView.estimatedRowHeight = 44.0
return UITableViewAutomaticDimension-10.0
} else { ...
I hope you have the dataSource for your tableView and returning dataSource.count in your numberOfRowsAt method.
Now implement the heightForRowAt method to return proper value like below:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if lastRow {
return 44
}else{
return UITableViewAutomaticDimension
}
}
And do not set the tableView.estimatedRowHeight = 44.0 at every place, the only place you need to set is in your viewDidLoad method.
Think you're running into issues with the way a table view works. The table view implements a form of lazy loading so that there aren't too many views in memory.
Try using the table view delegate methods to "report" correct heights based off the cell's index.
https://developer.apple.com/reference/uikit/uitableviewdelegate/1614998-tableview
What you have to do is set a flag on your cell, called isLast or something like that. Then, when returning your cells from your table view delegate you should set that flag to true if it is indeed the last cell of your table.
Then, on your cell you should be able to modify your constraints based on that flag, such that the cell has 10 points less then the rest of the cells. I can't tell you exactly what constraint and how to modify it since I have no idea how your cell looks like.
I'd like to be able to fix the position of certain rows in a UITableView as the user scrolls.
Specifically, I have a table whereby certain rows are "headers" for the rows that follow, and I'd like the header to stay at the top of the screen as the user scrolls up. It would then move out of the way when the user scrolls far enough that the next header row would take its place.
A similar example would be the Any.DO app. The "Today", "Tommorrow" and "Later" table rows are always visible on the screen.
Does anyone have any suggestions about how this could be implemented?
I'm currently thinking of follow the TableDidScroll delegate and positioning my own cell in the appropriate place in front of the table view. The problem is that at other times I'd really like these cells to be real table cells so that they can be, for example, reordered by the user.
Thanks,
Tim
I've been playing about with this and I've come up with a simple solution.
First, we add a single UITableViewCell property to the controller. This should be initialize such that looks exactly like the row cells that we'll use to create the false section headers.
Next, we intercept scrolling of the table view
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
// Add some logic here to determine the section header. For example, use
// indexPathsForVisibleRows to get the visible index paths, from which you
// should be able to get the table view row that corresponds to the current
// section header. How this works will be implementation dependent.
//
// If the current section header has changed since the pervious scroll request
// (because a new one should now be at the top of the screen) then you should
// update the contents.
IndexPath *indexPathOfCurrentHeaderCell = ... // Depends on implementation
UITableViewCell *headerCell = [self.tableView cellForRowAtIndexPath:indexPathOfCurrentHeaderCell];
// If it exists then it's on screen. Hide our false header
if (headerCell)
self.cellHeader.hidden = true;
// If it doesn't exist (not on screen) or if it's partially scrolled off the top,
// position our false header at the top of the screen
if (!headerCell || headerCell.frame.origin.y < self.tableView.contentOffset.y )
{
self.cellHeader.hidden = NO;
self.cellHeader.frame = CGRectMake(0, self.tableView.contentOffset.y, self.cellHeader.frame.size.width, self.cellHeader.frame.size.height);
}
// Make sure it's on top of all other cells
[self.tableView bringSubviewToFront:self.cellHeader];
}
Finally, we need to intercept actions on that cell and do the right thing...
That's the default behavior for section headers in plain UITableView instances.
If you want to create a custom header, implement the tableView:viewForHeaderInSection: method in your table view delegate and return the view for your header.
Although you will have to manage sections and rows instead of just rows.
Swift 5 solution
var header: UIView?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(indexPath: indexPath) as UITableViewCell
header = cell.contentView
return cell
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let headerCell = tableView.cellForRow(at: IndexPath(row: 0, section: 0))
guard headerCell == nil || (headerCell!.frame.origin.y < self.tableView.contentOffset.y + headerCell!.frame.height/2) else {
header?.isHidden = true
return
}
guard let hdr = header else { return }
hdr.isHidden = false
hdr.frame = CGRect(x: 0, y: tableView.contentOffset.y, width: hdr.frame.size.width, height: hdr.frame.size.height)
if !tableView.subviews.contains(hdr) {
tableView.addSubview(hdr)
}
tableView.bringSubviewToFront(hdr)
}
I'm styling the UITableView in InAppSettingsKit and want to change the color of the header title:
The labels Without title and Text Field should be white. How can this be done?
Thanks.
This is an old question, but I think the answer needs to be updated.
This method does not involve defining and creating your own custom view.
In iOS 6 and up, you can easily change the background color and the text color by defining the
-(void)tableView:(UITableView *)tableView
willDisplayHeaderView:(UIView *)view
forSection:(NSInteger)
delegate method.
For example:
- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section
{
// Background color
view.tintColor = [UIColor blackColor];
// Text Color
UITableViewHeaderFooterView *header = (UITableViewHeaderFooterView *)view;
[header.textLabel setTextColor:[UIColor whiteColor]];
// Another way to set the background color
// Note: does not preserve gradient effect of original header
// header.contentView.backgroundColor = [UIColor blackColor];
}
Taken from my post here: https://happyteamlabs.com/blog/ios-how-to-customize-table-view-header-and-footer-colors/
Swift 5.0:
func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
if let header = view as? UITableViewHeaderFooterView {
header.textLabel?.textColor = .white
}
}
Implement the tableView:viewForHeaderInSection: method in the tableViewController. That will allow you to supply your own view for the headers, which can include a UILabel with whatever formatting you want, e.g.
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UILabel *customLabel = [[UILabel alloc] init];
customLabel.text = [self tableView:tableView titleForHeaderInSection:section];
return customLabel;
}
Remember to set the label frame to be tall enough to space out the sections. You may wish to embed the label inside a larger UIView and return that instead to simplify positioning (e.g. if you want increase the left-padding on the label).
It took me a few minutes to "translate" this to Swift, but here's a working equivalent in Swift 1.2 (iOS 8). Be sure and implement UITableViewDelegate in your class:
// MARK: - VIEW METHODS
override func viewDidLoad() {
tableView.delegate = self
}
// MARK: - TABLEVIEW DELEGATE METHODS
override func tableView(tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
let header = view as! UITableViewHeaderFooterView
header.textLabel.textColor = UIColor.whiteColor()
}
Swift 4 version:
func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
guard let headerView = view as? UITableViewHeaderFooterView else { return }
headerView.textLabel?.textColor = .red // any color
}
I found this:
How to change text color for Section Headers in a Grouped TableView in iPhone SDK?
It works a treat, and I combined it with a little bit of code from the link in the answer by #rishi, and it made my life so much easier!! Although I had to tweak the coordinations of the label view a bit, but it worked like a charm.
You can try following line of code in table cell creation method -
cell.textLabel.backgroundColor = //Your color;
cell.detailTextLabel.backgroundColor = //Your color;
You can refer following for more detail description, where you can find detail example of creating custom sectioned table similar to what you have mentioned -
http://undefinedvalue.com/2009/08/25/changing-background-color-and-section-header-text-color-grouped-style-uitableview
I want a space between two cell in table view,
I want cell like this,
How can i do that?
you can't set distance between cells directly, but you can set the height for header in section to achieve the same result.
1.set the numbers of cell you need as sections:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 3; // in your case, there are 3 cells
}
2.return only 1 cell for each section
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 1;
}
3.set the height for header in section to set space between cells
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 10.; // you can have your own choice, of course
}
4.set the header's background color to clear color, so it won't look weird
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView *headerView = [[UIView alloc] init];
headerView.backgroundColor = [UIColor clearColor];
return headerView;
}
The Best way to get space between two cells in TableView, declare the numbers of sections you want in delegate method of numberofsections this way
For example you have array of 10 objects
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [array count]; //array count returns 10
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 1;// this should be one because it will create space between two cells if you want space between 4 cells you can modify it.
}
Then the important point is in cellForRowAtIndexPath delegate method you need to use indexpath.section but not indexpath.row
cell.textLabel.text=[NSString stringWithFormat:#"%#",[array objectAtIndex:indexPath.section]];
That is is check your tableview for the space between two cells. Enjoy!
You can create a Sections of TableView also in the UITableView... This methods are compulsory so create sections and in each section you can create single cell as in your picture..
The multiple sections answer would work, but it's extremely brittle, and doesn't allow for actual sections. Instead, you should create a custom cell, or custom cell prototype that simply has a gap at the bottom and/or top.
Use your struts and springs in IB to maintain that uniform gap, and use heightForRowAtIndexPath to return a height that includes the gap.
Objective - C
UIView* separatorLineView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 3)];/// change size as you need.
separatorLineView.backgroundColor = [UIColor whiteColor];// you can also put image here
[cell.contentView addSubview:separatorLineView];
Swift 3 (This same will work for Swift 4 also)
var separatorLineView = UIView(frame: CGRect(x: 0, y: 0, width: 320, height: 3))
/// change size as you need.
separatorLineView.backgroundColor = UIColor.white
// you can also put image here
cell.contentView.addSubview(separatorLineView)
It worked for me.
If someone looking for Swift version. Here you go.
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 10; // space b/w cells
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return items.count // count of items
}
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = UIView()
header.userInteractionEnabled = false
header.backgroundColor = UIColor.clearColor()
return header
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
For people that looking for alternative way of showing gaps between cells without using sections, you may want to show alternate colours and height like below. Use clearColor for the spacing.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
if (indexPath.row % 2 == 1)
{
static NSString *CellIdentifier = #"cellID1";
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
cell.backgroundColor = [UIColor colorWhite];
return cell;
} else {
static NSString *CellIdentifier2 = #"cellID2";
UITableViewCell *cell2 = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier2];
if (cell2 == nil) {
cell2 = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier2];
}
cell2.backgroundColor = [UIColor clearColor];
return cell2;
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row % 2 == 1) {
return 40.0;
} else {
return 2.0;
}
}
Add these lines in the cellForRowAtIndexPath UITableViewDelegate method before returning the cell.
let separator = UIView(frame: CGRectMake(0, 0, cell!.bounds.size.width, 1))
separator.backgroundColor = UIColor.whiteColor()
cell.contentView.addSubview(separator)
Sometimes, you might really want to keep the tableview divided in rows, and have 1 section. For example, this could happen if you need to display a custom header for that table view that stays in place when you scroll through the section.
What I would recommend doing in this case, is returning a bigger float than the normal height of a cell in:
- (float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
and then making sure that the table style is Plain, and that the cell separator is none. You can do that in the XIB file itself, or in code:
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
self.tableView.style = UITableViewStylePlain;
Maybe also add the cell's selection style to none (otherwise it will look like you are selecting more than just the visible part of the cell).
cell.selectionStyle = UITableViewCellSelectionStyleNone;
This will give the impression of space between the cells, but at the same time keeping them as rows in one section (which sometimes is what you want).
I have used a simple and quick approach for this (using storyboard)
Add UITableView in your controller
Set the separator style to none (from attributes inspector)
Set the height of the row 5 pts more from top and bottom from the desired height
Now add an image in the cell, pin it from left and right but leave 5 pts space (for padding like feel) and set the background of the image same as the background you want for cell
When the table view will be loaded, it will feel like there are spaces between cells.
For spacing between cells like the ones in your screenshot, there is no need for custom cells (for their look anyway, like the gradient bkg and so on, this could anyway be a good idea, but this won't help for your spacing between cells)
To achieve this kind of spacing, simply use different sections in your UITableView.
[EDIT] Everything is explained In Apple's TableView Programming Guide (and that's worth reading it, as it contains a lot of stuff you should know about tableviews)
The best way to do that using xib file. give top and bottom constraint to it. for example, here I give bottom constraint 10 and make sure give perfect height to cell as shown as given below.here in code 'joinTableViewCell' is fileName of xib file.
extension JoinTableViewController: UITableViewDataSource,UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = Bundle.main.loadNibNamed("joinTableViewCell", owner: self, options: nil)?.first as! joinTableViewCell
cell.ImgView.layer.masksToBounds = true
cell.ImgView.cornerRadius(cell.ImgView.bounds.width / 2)
cell.lblName.text = "Christian Bell"
cell.lblJoin.text = "Joined"+"\t"+"Sep 3"
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 90
}
}
What you're trying to achieve, visually, would be the same as adding the content of each cell inside a container View with a gray background, and having that view inside the cell. I don't think there's a need to add spaces between cells.
I don't know why all the answers that complicated. KIS, only using storyboard I've put a UIView inside the tableCell Content View; now the UIView height is less than Content View height, and that's it!
Play with the Content View color and the UIView color to get the desired result.
My easy solution using Swift :
// Inside UITableViewCell subclass
override func layoutSubviews() {
let f = contentView.frame
let fr = UIEdgeInsetsInsetRect(f, UIEdgeInsetsMake(10, 10, 10, 10))
contentView.frame = fr
}
or one line code
override func layoutSubviews() {
contentView.frame = UIEdgeInsetsInsetRect(contentView.frame, UIEdgeInsetsMake(10, 10, 10, 10))
}
Result
* WORKING WITH IOS 9 XCODE 7.3 *
The most straight forward way to achieve this is to simply add this code to your cellForRowAtIndexPath method.
cell.separatorInset.left = 20.0
cell.separatorInset.right = 20.0
cell.separatorInset.top = 20.0
cell.separatorInset.bottom = 20.0
cell.layer.borderWidth = 3
cell.layer.cornerRadius = 20.0
cell.layer.borderColor = UIColor.flatSkyBlueColorDark().CGColor
Then go to your story board and click on the tableview. Go to identity inspector and change the View's background color to whatever border color was set in the method. Voila! Play with the values to get the desired output. Hope this helps!
Note: If using Chameleon library you must set the background color for the view in code, not via the story board plugin. For some reason the color seems to be off by a shade.
Well what I did is simply simulate the space by creating a simple custom cell that is a little larger than what I want (cell + gap/2), and then put all my cell's content in an inner-view.
Then put your cell's topmost view as the color of your background, and the inner-view as your actual cell. And you'll have the illusion of having space between cells, when it's actually just a larger cell with borders.
In Table View DataSource there are two methods named number of sections and number of rows
In sections
return 3;
In Rows
return 1;
You don't have to assign each section for each cell. Just create a UIView (container) inside your cell, margin it with cell's view. And we layout components like label, text, image on that container.
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return __titlesArray.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 1;
}
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 10;
}
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView *header = [[UIView alloc]init];
header.backgroundColor = [UIColor clearColor];
return header;
}
I use like:
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 194.0;
}
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
cell.contentView.backgroundColor = UIColor.clearColor()
let whiteRoundedView : UIView = UIView(frame: CGRectMake(0, 0, self.view.frame.size.width, 185))
whiteRoundedView.backgroundColor = UIColor( red: CGFloat(61.0/255.0), green: CGFloat(117.0/255.0), blue: CGFloat(147.0/255.0), alpha: CGFloat(1.0))
whiteRoundedView.layer.masksToBounds = false
whiteRoundedView.layer.cornerRadius = 3.0
whiteRoundedView.layer.shadowOffset = CGSizeMake(-1, 1)
whiteRoundedView.layer.shadowOpacity = 0.5
cell.contentView.addSubview(whiteRoundedView)
cell.contentView.sendSubviewToBack(whiteRoundedView)
}
Get Color Code RGB values from:
1) first create 2 sections in table view.
2) create an empty cell.
3) create the cell with data you want to display.
4) use the method
(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section==0) {
if (indexPath.row % 2 != 1) {
return 15.0;
} else {
return 100;
}
}
else
if (indexPath.row % 2 != 1) {
return 100.0;
} else {
return 15.0;
}
}
It will add space between the cells. It worked for me.
Here is my method with Swift 3:
In ViewDidLoad(), add:
self.tableView.rowHeight = 500.0
In tableview "CellForRowAt", add following:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
// Configure the cell...
var actualContentView = UIView()
actualContentView.frame = CGRect(x: 10, y: 10, width: cell.contentView.frame.width - 20, height: cell.contentView.frame.height - 20)
actualContentView.backgroundColor = UIColor.blue
cell.contentView.addSubview(actualContentView)
return cell
}
in your cellForRowAt
cell.layer.borderWidth = 2
cell.layer.cornerRadius = 10
cell.layer.borderColor = UIColor.systemBackground.cgColor
I suggest to create a custom UITableViewCell base class and use this class like below,
Create custom UITableViewCell class
Create a UIView in new class, this will act as the 'baseContentView' and it will be immediate child of 'UITableViewCell.contentView'
Adjust the top 'padding' on 'baseContentView' (this will be the separator/gap) from parent view (UITableViewCell.contentView)
Inherit all your custom classes from this class rather than UITableViewCell
Add all the content/subviews to 'baseContentView' rather than 'self.contentView'
You can play with the 'padding' as per your need.
I have checked all the answers but I think this is the easiest way:
UIView * theContentView = [UIView alloc]initWithFrame:CGRectMake(0,gap,width,height)];
theContentView.backgroundcolor = [UIColor lightGrayColor];//contentColor
cell.backgroundcolor = [UIColor blackColor];//gapColor
[cell addSubview: theContentView]
The prototype code says You can create a subview to show the content of cell and the rest is the gap as you wish.
Is there any way to know if a tableview cell is currently visible?
I have a tableview whose first cell(0) is a uisearchbar.
If a search is not active, then hide cell 0 via an offset.
When the table only has a few rows, the row 0 is visible.
How to determine if row 0 is visible or is the top row?
UITableView has an instance method called indexPathsForVisibleRows that will return an NSArray of NSIndexPath objects for each row in the table which are currently visible. You could check this method with whatever frequency you need to and check for the proper row. For instance, if tableView is a reference to your table, the following method would tell you whether or not row 0 is on screen:
-(BOOL)isRowZeroVisible {
NSArray *indexes = [tableView indexPathsForVisibleRows];
for (NSIndexPath *index in indexes) {
if (index.row == 0) {
return YES;
}
}
return NO;
}
Because the UITableView method returns the NSIndexPath, you can just as easily extend this to look for sections, or row/section combinations.
This is more useful to you than the visibleCells method, which returns an array of table cell objects. Table cell objects get recycled, so in large tables they will ultimately have no simple correlation back to your data source.
To checking tableview cell is visible or not use this code of line
if(![tableView.indexPathsForVisibleRows containsObject:newIndexPath])
{
// your code
}
here newIndexPath is IndexPath of checking cell.....
Swift 3.0
if !(tableView.indexPathsForVisibleRows?.contains(newIndexPath)) {
// Your code here
}
I use this in Swift 3.0
extension UITableView {
/// Check if cell at the specific section and row is visible
/// - Parameters:
/// - section: an Int reprenseting a UITableView section
/// - row: and Int representing a UITableView row
/// - Returns: True if cell at section and row is visible, False otherwise
func isCellVisible(section:Int, row: Int) -> Bool {
guard let indexes = self.indexPathsForVisibleRows else {
return false
}
return indexes.contains {$0.section == section && $0.row == row }
} }
Swift Version:
if let indices = tableView.indexPathsForVisibleRows {
for index in indices {
if index.row == 0 {
return true
}
}
}
return false
Another solution (which can also be used to check if a row is fully visible) would be to check if the frame for the row is inside the visible rect of the tableview.
In the following code, ip represents the NSIndexPath:
CGRect cellFrame = [tableView rectForRowAtIndexPath:ip];
if (cellFrame.origin.y<tableView.contentOffset.y) { // the row is above visible rect
[tableView scrollToRowAtIndexPath:ip atScrollPosition:UITableViewScrollPositionTop animated:NO];
}
else if(cellFrame.origin.y+cellFrame.size.height>tableView.contentOffset.y+tableView.frame.size.height-tableView.contentInset.top-tableView.contentInset.bottom){ // the row is below visible rect
[tableView scrollToRowAtIndexPath:ip atScrollPosition:UITableViewScrollPositionBottom animated:NO];
}
Also using cellForRowAtIndexPath: should work, since it returns a nil object if the row is not visible:
if([tableView cellForRowAtIndexPath:ip]==nil){
// row is not visible
}
Note that "Somewhat visible" is "visible". Also, in viewWillAppear, you might get false positives from indexPathsForVisibleRows as layout is in progress, and if you're looking at the last row, even calling layoutIfNeeded() won't help you for tables. You'll want to check things in/after viewDidAppear.
This swift 3.1 code will disable scroll if the last row is fully visible. Call it in/after viewDidAppear.
let numRows = tableView.numberOfRows(inSection: 0) // this can't be in viewWillAppear because the table's frame isn't set to proper height even after layoutIfNeeded()
let lastRowRect = tableView.rectForRow(at: IndexPath.init(row: numRows-1, section: 0))
if lastRowRect.origin.y + lastRowRect.size.height > tableView.frame.size.height {
tableView.isScrollEnabled = true
} else {
tableView.isScrollEnabled = false
}
Swift:
Improved #Emil answer
extension UITableView {
func isCellVisible(indexPath: IndexPath) -> Bool {
guard let indexes = self.indexPathsForVisibleRows else {
return false
}
return indexes.contains(indexPath)
}
}
IOS 4:
NSArray *cellsVisible = [tableView indexPathsForVisibleRows];
NSUInteger idx = NSNotFound;
idx = [cellsVisible indexOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop)
{
return ([(NSIndexPath *)obj compare:indexPath] == NSOrderedSame);
}];
if (idx == NSNotFound)
{
Best to check by Frame
let rectOfCellInSuperview = yourTblView.convert(yourCell.frame, to: yourTblView.superview!) //make sure your tableview has Superview
if !yourTblView.frame.contains(rectOfCellInSuperview) {
//Your cell is not visible
}
Swift 2023 simple solution, for a given index path:
tableView.indexPathsForVisibleItems.contains(cellIndexPath)
For me the problem was that indexPathsForVisibleRows doesn't take tableView insets into account. So for example if you change insets on keyboard appearance, rows covered by keyboard still are considered visible. So I had to do this:
func isRowVisible(at indexPath: IndexPath, isVisibleFully: Bool = false) -> Bool {
let cellRect = tableView.rectForRow(at: indexPath)
let cellY = isVisibleFully ? cellRect.maxY : cellRect.minY
return cellY > tableView.minVisibleY && cellY < tableView.maxVisibleY
}
And this is UITableView extension:
extension UITableView {
var minVisibleY: CGFloat {
contentOffset.y + contentInset.top
}
var maxVisibleY: CGFloat {
contentOffset.y + bounds.height - contentInset.bottom
}
}