0

Update: My Problem was that the timer was not firing during the scroll and the fact that for a reason now unclear to me i was under the impression that scrollViewDidScroll would not fire all the time.


I have a UITableView with the same height as the containing view. While the UITableView content is moving i need to know which cell is closest to the vertical center of the containing view.

Things which could make a potential solution: 1. The number of items is fix 2. The cell-heights are all equal and known

I tried a number of solutions including reading the contentOffset, rectForRowAtIndexPath and indexPathForRowAtPoint. Also this idea which seems to be the same problem: Determine coordinates of a UITableViewCell while scrolling

But all this solution suffer from the fact that the values of those (contentOffset, rectForRowAtIndexPath and indexPathForRowAtPoint) are only updated after the UITableView stopped moving. But since i want to read data related to the cell in the middle of the screen also while it moves this is of no help.

Sidenote: I use a NSTimer Interval to check the offset.

Update (Code): This is the code which atleast works after the table came to a rest. The setup is simple. iPad sized ViewController in Landscape, Table with the same height as the ViewController wired to self.tableView. That is all.

import UIKit

class ViewControllerTwoScroll: UIViewController {

    let cellIdentifier = "cellIdentifier"
    let cellHeight : CGFloat = 768 / 5
    var tableData = [String]()
    var magicNumber = 0

    @IBOutlet var tableView: UITableView?
    @IBOutlet var devLabelOffset: UILabel?

    override func viewDidLoad() {
        super.viewDidLoad()
        self.tableView?.registerClass(UITableViewCell.self, forCellReuseIdentifier: self.cellIdentifier)
        self.tableData.append("");
        self.tableData.append("");
        for index in 0...100 {
            self.tableData.append("Item \(index)")
        }
        self.tableData.append("");
        self.tableData.append("");

        // Choose what you like more
        self.tableView?.decelerationRate = UIScrollViewDecelerationRateNormal
        //self.tableView?.decelerationRate = UIScrollViewDecelerationRateFast

        /*** Timer to check the middel cell in a interval ***/
        var timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: Selector("checkIndexFokus"), userInfo: nil, repeats: true)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    /*** Table Basic-Stuff ***/

    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return tableData.count
    }

    func tableView(tableView: UITableView!, willDisplayCell cell: UITableViewCell!, forRowAtIndexPath indexPath: NSIndexPath!) {
        cell.backgroundColor = UIColor.clearColor()
        cell.backgroundView = UIView()
        cell.selectedBackgroundView = UIView()
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        var cell = tableView.dequeueReusableCellWithIdentifier(self.cellIdentifier) as UITableViewCell
        cell.textLabel?.text = self.tableData[indexPath.row]
        return cell
    }

    func tableView( tableView: UITableView,  heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        return cellHeight;
    }

    /*** Observe change of cell closest to the center ***/

    func checkIndexFokus() {
        /*** no solution yet. But if i would know the position of the first cell that
        would be enough to calculate the cell that is actually in the middle ***/
        var rectInTableView = self.tableView?.rectForRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0))
        rectInTableView = self.tableView?.convertRect(rectInTableView!, toView: self.tableView?.superview)
        /*** this shows the correct value for the first cell as i can say but only after the table came to a rest ***/
        self.devLabelOffset?.text = "\(rectInTableView!.origin.y)"
    }

    /*** Table snapping ***/

    func scrollViewWillEndDragging(scrollView: UIScrollView!, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
        targetContentOffset.memory.y = floor(targetContentOffset.memory.y / cellHeight) * cellHeight
    }

}
Community
  • 1
  • 1
Alex
  • 541
  • 5
  • 19
  • So the aim isn't really to get the cell, it's to get the index path of the cell closest to centre so you can query your data model? You should also show the code that you have which is closest to working. – Wain Mar 08 '15 at 11:57
  • Actually i could work around if i only know the cell but i will update my question with my code since basically you are right, having the index would be best. – Alex Mar 08 '15 at 12:01

1 Answers1

0

contentOffset is updated during scrolling, so in the scrollViewDidScroll method you can get the value. You also say that all of the rows are the same height so a quick little bit of maths can find the row at that content offset (plus half the view height).

If your current attempts aren't working then it's more likely to be an issue with the timer, probably that it isn't firing during the scrolling. It isn't clear why you're using a timer, but you need to configure it appropriately (run loop) to fire at the same time as the scroll happens.

It's probably best not to use a timer and just do the calculation in the scroll delegate method (possibly with a conditional on doing it too often).

I expect that in the scroll delegate method indexPathForRowAtPoint should work and should be the least lines of code solution to the problem (because you always use the same point and don't worry directly about the content offset).

Wain
  • 118,658
  • 15
  • 128
  • 151
  • Ok that could be the problem, i updated my post with the code i am using. Why i use a timer? Well i need to know this offset all the time - during the period in which the user drags and during the period when the table moves by itself - because i want to animate things depending in the data and refreshing data also while the table content is moving. – Alex Mar 08 '15 at 12:12
  • But you are completely right, the Timer is not firing while the table moves.. But i can`t see how i could use delegate methods when i need the data all the time - for example if i want to animate the content of the tablecell in relation on how close it is to the center (while the user drags or the table moves by itself ..) – Alex Mar 08 '15 at 12:16
  • So you expressly shouldn't use a timer to do that, you should put your logic in the scroll delegate method as it us updated all the time while the view is scrolling. – Wain Mar 08 '15 at 12:23
  • Thanks, i don`t know how i could have missed the fact that scrollViewDidScroll really fires all the time. Would have saved me alot including a downvoted question ;) – Alex Mar 08 '15 at 12:29