6

I have a view controller that contains a navigation bar, a table view and a tool bar. I included the UITableViewDelegate in the view controller, and correctly assigned the table's data source and delegate to the view controller through the storyboard. The table view loads its data from a remote database, once the table scrolls to the last cell more data is loaded into the table. I achieved this by using the scrollViewDidScroll and indexPathForRowAtPoint methods as outlined in the following post: How to know when UITableView did scroll to bottom in iPhone. However, when I run the app and scroll through the table the only index path returned by indexPathForRowAtPoint is the one that was located at the specified point at the time of table load. Here is the code and the output I get when I scroll:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    CGPoint bottomPoint = CGPointMake(160, 430);
    CGPoint topPoint = CGPointMake(160, 10);

    NSLog(@"%d", [[_tableView indexPathForRowAtPoint:bottomPoint] row]);

}

Every time I scroll the following is outputted:

2013-06-08 00:56:45.006 Coffee[24493:907] 3
2013-06-08 00:56:45.012 Coffee[24493:907] 3
2013-06-08 00:56:45.040 Coffee[24493:907] 3
2013-06-08 00:56:45.069 Coffee[24493:907] 3
2013-06-08 00:56:45.088 Coffee[24493:907] 3
2013-06-08 00:56:45.105 Coffee[24493:907] 3
2013-06-08 00:56:45.135 Coffee[24493:907] 3
2013-06-08 00:56:45.144 Coffee[24493:907] 3
2013-06-08 00:56:45.173 Coffee[24493:907] 3
2013-06-08 00:56:45.180 Coffee[24493:907] 3

Where 3 is the indexPath.row of the cell the bottom point is on when the controller loads. What am I doing wrong and why is this happening? Does it have anything to do with the fact that the UITableView is located inside a parent view controller?

Community
  • 1
  • 1
ScottOBot
  • 839
  • 3
  • 16
  • 37

4 Answers4

12

bottomPoint is looking for a location inside your scrollView. Your scrollView contains a table, and all the cells are in the SAME PLACE all the time, relative to your scrollView. That is why you always get the same cell at that point.

When you scroll, the cells don't move in the scrollView, the scrollView "moves" relative to its parent. This is done by having its contentOffset changed.

If you add your scrollView's y content offset to your bottomPoint, you'll get the point that you are probably really looking for in your scrollView.

like this:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    CGPoint bottomPoint = CGPointMake(160, 430) - scrollView.contentOffset.y;
    CGPoint topPoint = CGPointMake(160, 10);

    NSLog(@"%d", [[_tableView indexPathForRowAtPoint:bottomPoint] row]);

}
HalR
  • 11,411
  • 5
  • 48
  • 80
3

As you can see from the Apple's Documentation, the Method indexPathForRowAtPoint: is refer to a point in the local coordinate system of the receiver (the table view's bounds), BUT NO the table view's frame. As the bottomPoint you set is in the table view's frame coordinate system, you will not get the right indexPath. HalR's answer gives the bottomPoint in the table view's bounds coordinate system.

indexPathForRowAtPoint:

Returns an index path identifying the row and section at the given point.

- (NSIndexPath *)indexPathForRowAtPoint:(CGPoint)point

Parameters
point: a point in the local coordinate system of the receiver (the table view's bounds).

Return Value
An index path representing the row and section associated with point or nil if the point is out of the bounds of any row.

FYI different between Frame and Bounds

Community
  • 1
  • 1
lu yuan
  • 7,207
  • 9
  • 44
  • 78
1

Here is the class to implement the feature.

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

@IBOutlet weak var tableView: UITableView!

override func viewDidLoad() {
    super.viewDidLoad()

    let flagView = UIView(frame: CGRect(x: 0, y: 100.0, width: self.view.frame.width, height: 20.0))

        flagView.backgroundColor = .blue

        self.view.addSubview(flagView)

    // Do any additional setup after loading the view, typically from a nib.
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 20
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
   let cell = UITableViewCell()
    cell.textLabel?.text = "Cell number: \(indexPath.row)"
    return cell
}

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    if scrollView.contentOffset.y > 0 {
        let currentPoint = CGPoint(x: self.view.frame.width - 100, y: 100.0 + scrollView.contentOffset.y)
        print(" current index:  \(String(describing: tableView.indexPathForRow(at: currentPoint)?.row))")
    }
}

}

Illya Krit
  • 925
  • 1
  • 8
  • 9
0
let bottomPoint = CGPoint(x: tableView.frame.midX, y: tableView.frame.maxY)
let converted = tableView.convert(point, from: tableView.superview)
let indexPathForBottomCell = tableView.indexPathForItem(at: converted)

This should work too, and may be easier to understand. All the additional spaces (like bottom inset) would be handled differently for each case.