17

I would like to load my tableview backwards, meaning the the tableView loads from bottom and scroll up to see more content.

First, I tried reversing the dataSource array. The contents are reversed, but, it still loads from the top, and the user has to scroll down to see more content.

I then tried loading the tableView from the bottom in viewWillAppear:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    if (self.conversationTableView.contentSize.height > self.conversationTableView.frame.size.height) {
        let offset = CGPoint(x: CGFloat(0), y: CGFloat(self.conversationTableView.contentSize.height - self.conversationTableView.frame.size.height))
        self.conversationTableView.setContentOffset(offset, animated: true)
    }
}

This attempt, however does not work since I am asynchronously downloading images (I'm using NSTextAttachment, which fills my UITextView within each cell). Everytime my placeholder is replaced with the real downloaded image, it causes the tableView content offset to shift (the placeholder's image height is almost always smaller than the height of downloaded image). So setting the contentOffset this way does not work.

How does one load the tableView from the bottom and scroll up to see more content?

Josh O'Connor
  • 4,694
  • 7
  • 54
  • 98
  • What do you mean by "load from the bottom"? Do you want to build an animation of the table coming up from the bottom? – Pedro Castilho Apr 18 '17 at 19:46
  • No. When the table loads, I want the Y offset to already be at the bottom. The user then scrolls up to see more content – Josh O'Connor Apr 18 '17 at 19:47
  • You want something like reversed tableview right? You can check this [extension](https://github.com/marty-suzuki/ReverseExtension). – ridvankucuk Apr 18 '17 at 19:56

2 Answers2

68

The best way was to flip the tableView and it's cells. This way, if a cell were to change size (due to things like asynchronous downloading), you will still load from the bottom of the tableview than at an offset.

//In ViewDidLoad
conversationTableView.transform = CGAffineTransform(rotationAngle: -(CGFloat)(Double.pi));

//In cellForRowAtIndexPath
cell.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi));

Also: If you have a headerView, simply set it to the tableView.tableFooterView:

var tableHeaderView = UIView(frame: headerFrame)
tableHeaderView.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi));
conversationTableView.tableFooterView = tableHeaderView

And if you have a footerView and headerView, just set the header to the footer, and the footer to the header.

Edit: If you want to flip the scroll indicator:

conversationTableView.scrollIndicatorInsets = UIEdgeInsetsMake(0.0, 0.0, 0.0, conversationTableView.bounds.size.width - 8.0)
Suresh Mopidevi
  • 919
  • 3
  • 9
  • 24
Josh O'Connor
  • 4,694
  • 7
  • 54
  • 98
  • 2
    Smart and creative solution! Works really well for me. One thing I noticed was that the scrollbar will now appear on the left side of the screen rather than on the right. To solve this is I adjusted the conversationTableView.scrollIndicatorInsets. Maybe it's worth adding this to the answer? i.e. conversationTableView.scrollIndicatorInsets = UIEdgeInsetsMake(0.0, 0.0, 0.0, conversationTableView.bounds.size.width - 8.0) – Bocaxica Feb 13 '18 at 11:22
  • 2
    Any idea how to get the scrollsToTop behavior to work correctly with the transform? As it is now, tapping the status bar will cause the view to scroll down, not up, which is unconventional and counter-intuitive. – T'Pol Apr 13 '18 at 21:49
  • just scroll to the bottom instead of the top – Josh O'Connor Apr 14 '18 at 00:03
  • 3
    Just madness apple does not have a native way to do this. – J. Doe Sep 22 '18 at 15:52
  • 1
    while rotating , it animates. How to stop animating while rotating @JoshO'Connor – Suresh Mopidevi Dec 28 '18 at 05:34
  • Is there any way to transform the Delete option also? I've transform the tableview and cell but don't know how to transform the Delete option. – Gautam Sareriya Nov 26 '19 at 08:13
  • With ios 13 swiping down will dismiss the top view controller. With this solution swiping up causes the view controller to start to dismiss downward. Anyone know how to handle this? – kezor Aug 16 '20 at 20:38
  • If you are flipping the scroll indicator, put that line of code in `viewDidLayoutSubviews` or it may not look right on all phones since the view sizes are not fully calculated. – Micro Oct 11 '22 at 22:47
  • Brilliant!!!. This really helped me. I was struggling with scrolling animations. – Musa almatri Dec 21 '22 at 06:47
2

In your viewWillAppear method, do the following:

tableView.scrollToRowAtIndexPath(bottomIndexPath, atScrollPosition: .Bottom,
      animated: true)

Where bottomIndexPath is the indexPath for the final row in your table view. If you have an array of length n and only one section in your table, this will usually be IndexPath(item: n-1, section: 0), but be sure to check your specific UITableView.

This will ensure you start off at the bottom of the tableView. Having done that, all you need to do is ensure your cellForRowAtIndexPath method on your UITableViewDataSource returns the data in reverse and you'll get the effect you want.

Pedro Castilho
  • 10,174
  • 2
  • 28
  • 39
  • This works if I am not asynchronously downloading images and placing them into my textView (which fils a cell). However since I am downloading images, the cells height will change after the scrollToRow method is called. – Josh O'Connor Apr 18 '17 at 20:55
  • Do you get some kind of callback when the image download is done? You could just scroll to the bottom of the table/bottom of the current row with no animation when that happens. – Pedro Castilho Apr 18 '17 at 21:09
  • 1
    Yes I do, but changing the offset everytime an image is downloaded isnt the most efficient way of solving this problem. I just posted my solution if you were curious. Thanks for your help – Josh O'Connor Apr 18 '17 at 21:16