2

Google Primer app for iOS has a table view scrolling effect where the cells stack on top of each other while scrolling. Primer application : you can kinda see the effect if you scroll on their page... The effect I am talking about is in the featured lessons of Primer. I am trying to reproduce it with a tableview.

I have tried playing with the frame of the cell on the viewdidscroll event. I got it working but it gets very jumpy/jerky when moving up in a certain way. Also, when the jerk happens, the frame gets offset incorrectly. I was only able to do it on the frame of a label inside the cells. My cells are really big(more then half of the screen).

How can I eliminate the jumping/jerk ? How can I animate the container view position instead of the label position ?

Current animation on the scrollViewDidScroll with a label :

override func scrollViewDidScroll(scrollView: UIScrollView) {
    if (self.tableView.contentOffset.y < 0) { return }

    let offsetYDifference =  oldYOffset - self.tableView.contentOffset.y 
    oldYOffset = self.tableView.contentOffset.y

    let cell = self.tableView.visibleCells.first!

    var newOriginY = (cell.textLabel?.frame.origin.y)! - offsetYDifference
    if (newOriginY < 0) { //Cells get reused.
        newOriginY = newOriginY + cell.frame.height
    }

    cell.textLabel?.frame = CGRect(x: (cell.textLabel?.frame.origin.x)!,
        y: newOriginY,
        width: (cell.textLabel?.frame.width)!,
        height: (cell.textLabel?.frame.height)!)
}

Custom paging-like behavior with paging enabled set to off on the storyboard :

override func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {

    let offsety = targetContentOffset.memory.y
    var smallestCellDiffToContentOffset = abs(offsety - (self.tableView.visibleCells.first?.frame.origin.y)!)

    var closestCellOrigin = self.tableView.visibleCells.first?.frame.origin.y

    self.tableView.visibleCells.forEach { (cell) -> () in
        if (abs(offsety - cell.frame.origin.y) <= smallestCellDiffToContentOffset) {
            smallestCellDiffToContentOffset = abs(offsety - cell.frame.origin.y)
            closestCellOrigin = cell.frame.origin.y
        }
    }
    targetContentOffset.memory.y = closestCellOrigin!
}

I am a beginner swift developper and I am really clueless on what the do. Thanks in advance.

Community
  • 1
  • 1
AlexB
  • 3,518
  • 4
  • 29
  • 46

3 Answers3

3

In essence what you're seeing is the UITableView is resisting your efforts to create this animation. Rightly so, changing the frame of the cell should be left to the tableview itself.

What you instead need is a UICollectionView with a custom layout object that controls the position of the cells. This'll be a fair bit of work, but you are allowed finer grain control that the UITableView doesn't give you.

Checkout this tutorial for a brief primer on custom layouts for collection views. I recommend watching the 2012 WWDC video Advanced Collection Views and Building Custom Layouts as well.

  • Great answer ! Loooks like a good solution. This will give me way more control. Thankyou. Haven't thought about it. – AlexB Jan 07 '16 at 22:28
  • No problem. Alternatively if it's your desire to mimic the Primer application exactly, you're best bet would be to create your own navigation controller that uses [UIViewController containment](https://www.objc.io/issues/1-view-controllers/containment-view-controller/). Having UIViewControllers in either UITableViewCell's or UICollectionViewCell's can be hairy and is generally discouraged. For normal view's though, it's fine. – Christopher Kevin Howell Jan 08 '16 at 09:21
2

Try using CGAffineTransformMakeTranslation. It's a visual-only effect, so it won't mess up what the table view is trying to do with the cell's frame. Something like this (maybe in viewDidScroll()):

for cell in tableView.visibleCells() {
  let cellOffset = tableView.contentOffset.y * someMagicFormula
  cell.transform = CGAffineTransformMakeTranslation(0.0, cellOffset)
}
Dave Batton
  • 8,795
  • 1
  • 46
  • 50
-2

If you will use UIScrollView, instead of UITableView functionality, then you can enable pagination in scrollView (with adding custom views in it) with vertical bouncing effect.

Else If you want to use UITableView, you could use a UIScrollViewDelegate and implement something like this.

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
                     withVelocity:(CGPoint)velocity
              targetContentOffset:(inout CGPoint *)targetOffset
{
  UITableView *tableView = (UITableView*)scrollView;
  NSIndexPath *topIndexPathAfterScrolling = [tableView indexPathForRowAtPoint:*targetContentOffset];
  CGRect rect = [tableView rectForRowAtIndexPath:indexPathOfTopRowAfterScrolling];
  targetOffset->y=rect.origin.y;
}

Here you can adjust the scroll action at particular contentOffset: "targetOffset"

Caleb Kleveter
  • 11,170
  • 8
  • 62
  • 92
Ankit Thakur
  • 4,739
  • 1
  • 19
  • 35
  • This doesn't answer the question. I already have the custom paging. And this is a swift question. – AlexB Jan 06 '16 at 19:34