swifttableviewcustom-paging

Custom Paging Size for TableView with Header in Swift (Scroll One Cell At a Time)


I have a UITableView with multiple sections, each with a header. The table's sizing is such that one header and one cell take up the entire frame of the table. I would like to allow the user to scroll the table only one cell at a time. When I set paging = enabled, the scrolling does not work as intended, since the table scrolls one entire table frame at a time, rather than one cell at a time. This causes an undesired offset that keeps getting larger as you scroll the table.

My research thus far suggests that I need to override scrollViewWillBeginDragging. See for example UITableView with custom paging-like behaviour. But every post that I read on this topic must have been before Swift, because it's all in Objective C.

Please suggest Swift code to accomplish single cell paging for a tableview with section headers. Although paging itself may need to be disabled, the solution should be as close to true paging as possible.

Below is code for my table's sizing. The table's frame size is 475.

func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    if (condition == true) {
        return CGFloat(425)
    }
    else {
        return CGFloat(375)
    }
}

func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {

    if (condition == true) {
        return CGFloat(47)
    }
    else {
        return CGFloat(90)
    }
}

UPDATE: The top answer here (Force UITableView to Scroll to Tops of Cells) also seems relevant. But, again, it's all in Objective C. Should the answers in any of these links prove correct, a simple translation of that answer into Swift would suffice.

UPDATE 2 I have nearly figured this out. But the edge cases aren't working (i.e., at the very top and very bottom of the table). That is, I get a paging-like scrolling action, except when I try to scroll down to the last cell in the table. No snapping to that last cell. It just scrolls as normal. I suspect it has something to do with the frame.origin.y of that last cell.

Please help me figure out these edges cases. Code below:

viewDidLoad() {
      self.tableView.decelerationRate = UIScrollViewDecelerationRateFast
}

var lastContentOffset:CGPoint!
var initialIndexPath:NSIndexPath?
func scrollViewWillBeginDragging(scrollView: UIScrollView) {
    lastContentOffset = CGPointMake(scrollView.contentOffset.x, scrollView.contentOffset.y)
    var visibleCellIndexes = tableView.indexPathsForVisibleRows() as! [NSIndexPath]
    initialIndexPath = visibleCellIndexes[0]
}



var scrolledToPath:NSIndexPath?
func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    if (scrollView == self.tableView) {


        var lastIndexPath:NSIndexPath!
        var nextIndexPath:NSIndexPath!
        if let p = scrolledToPath {
            lastIndexPath = scrolledToPath
        }
        else if let u = initialIndexPath {
            lastIndexPath = initialIndexPath
        }
        if (lastContentOffset.y <= scrollView.contentOffset.y) {
            // scrolling down
            if (lastIndexPath.row == numRows(lastIndexPath.section)-1) {
                // last row in section
                if (lastIndexPath.section == numSections(self.tableView)-1) {
                    // last  section
                    nextIndexPath = lastIndexPath
                }
                else {
                    nextIndexPath = NSIndexPath(forRow: 0, inSection: lastIndexPath.section+1)
                }
            }
            else {
                nextIndexPath = NSIndexPath(forRow: lastIndexPath.row+1, inSection: lastIndexPath.section)
            }
        }
        else if (lastContentOffset.y > scrollView.contentOffset.y) {
            // scrolling up
            if (lastIndexPath.row == 0) {
                // first row in section
                if (lastIndexPath.section == 0) {
                    // first section
                    nextIndexPath = lastIndexPath
                }
                else {
                    nextIndexPath = NSIndexPath(forRow: numRows(lastIndexPath.section-1)-1, inSection: lastIndexPath.section-1)
                }
            }
            else {
                nextIndexPath = NSIndexPath(forRow: lastIndexPath.row-1, inSection: lastIndexPath.section)
            }

        }

        scrolledToPath = nextIndexPath

        var headerHeight = CGFloat(47)
        if (condition == true) {
            headerHeight = CGFloat(90)
        }

        var rectOfNextIndexPath:CGRect = self.tableView.rectForRowAtIndexPath(nextIndexPath)
        targetContentOffset.memory.y = rectOfNextIndexPath.origin.y - headerHeight

    }
}

Solution

  • Fixed. Sizing in my tableview was messing up the end cases. After I made sure the cell sizes were correct, it worked great. Below is the solution for page-like scrolling of tableview in swift.

    viewDidLoad() {
          self.tableView.decelerationRate = UIScrollViewDecelerationRateFast
    }
    
    var lastContentOffset:CGPoint!
    var initialIndexPath:NSIndexPath?
    func scrollViewWillBeginDragging(scrollView: UIScrollView) {
        lastContentOffset = CGPointMake(scrollView.contentOffset.x,     scrollView.contentOffset.y)
        var visibleCellIndexes = tableView.indexPathsForVisibleRows() as! [NSIndexPath]
        initialIndexPath = visibleCellIndexes[0]
    }
    
    
    
    var scrolledToPath:NSIndexPath?
    func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    
        var lastIndexPath:NSIndexPath!
        var nextIndexPath:NSIndexPath!
        if let p = scrolledToPath {
            lastIndexPath = scrolledToPath
        }
        else if let u = initialIndexPath {
            lastIndexPath = initialIndexPath
        }
        if (lastContentOffset.y <= scrollView.contentOffset.y) {
            // scrolling down
            if (lastIndexPath.row == numRows(lastIndexPath.section)-1) {
                // last row in section
                if (lastIndexPath.section == numSections(self.tableView)-1) {
                    // last  section
                    nextIndexPath = lastIndexPath
                }
                else {
                    nextIndexPath = NSIndexPath(forRow: 0, inSection: lastIndexPath.section+1)
                }
            }
            else {
                nextIndexPath = NSIndexPath(forRow: lastIndexPath.row+1, inSection: lastIndexPath.section)
            }
        }
        else if (lastContentOffset.y > scrollView.contentOffset.y) {
            // scrolling up
            if (lastIndexPath.row == 0) {
                // first row in section
                if (lastIndexPath.section == 0) {
                    // first section
                    nextIndexPath = lastIndexPath
                }
                else {
                    nextIndexPath = NSIndexPath(forRow: numRows(lastIndexPath.section-1)-1, inSection: lastIndexPath.section-1)
                }
            }
            else {
                nextIndexPath = NSIndexPath(forRow: lastIndexPath.row-1, inSection: lastIndexPath.section)
            }
    
        }
    
        scrolledToPath = nextIndexPath
    
        var headerHeight = CGFloat(47)
        if (condition == true) {
            headerHeight = CGFloat(90)
        }
    
        var rectOfNextIndexPath:CGRect = self.tableView.rectForRowAtIndexPath(nextIndexPath)
        targetContentOffset.memory.y = rectOfNextIndexPath.origin.y - headerHeight
    
    
    }