1

I am trying to create a comments / messages tableView using Swift and would like for the UITableViewController to show the last tableViewCell when the user clicks that "comment room"

I am pulling data from a remote source, (Firebase in this case) to populate each of the tableViewCells. Once I pull all the data, I configure each cell with the appropriate information. This is done without any error whatsoever.

However, by default, the tableview shows the cell at the top

I've tried using these to make the tableView show the last cell but it still performs a visible "scrolling" effect which I would like to remove. For example, take the iMessage app, Instagram's or Snapchat's direct messaging feature. Upon clicking a "chat or comment room", the resulting table (or collectionview) immediately displays the last cell without visibly scrolling to it

Here is what I tried:

var chatRoomMessages: [FIRDataSnapshot] = []

override func viewDidLoad() {
    super.viewDidLoad()

    fetchChatRoomCommentsFromServer(chatRoomId: self.chatRoomId) { (snap) in
        self.chatRoomMessages.append(snap)
        self.tableView.reloadData()

    }
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    moveToLastComment()
}

func fetchChatRoomCommentsFromServer(chatRoomId: String, callback: @escaping (FIRDataSnapshot) -> ()) {
    DataService.dataService.CHAT_ROOM_REF.child(chatRoomId).child("messages").observe(.childAdded, with: { (snapshot) in
        DataService.dataService.MESSAGES_REF.child(snapshot.key).observe(.value, with: { (snap) in
            callback(snap)
        })
    })
}

func moveToLastComment() {
    if self.tableView.contentSize.height > self.tableView.frame.height {
        // First figure out how many sections there are
        let lastSectionIndex = self.tableView!.numberOfSections - 1

        // Then grab the number of rows in the last section
        let lastRowIndex = self.tableView!.numberOfRows(inSection: lastSectionIndex) - 1

        // Now just construct the index path
        let pathToLastRow = NSIndexPath(row: lastRowIndex, section: lastSectionIndex)

        // Make the last row visible
        self.tableView?.scrollToRow(at: pathToLastRow as IndexPath, at: UITableViewScrollPosition.bottom, animated: true)
    }
}

The messages are laoded properly and the view does scroll to the last tableViewCell as intended....BUT...I want the view to already appear at that last cell. Along with the three app examples, I've seen other apps do the same thing.

Any help would be greatly appreciated

EDIT: 3:26pm November 17

I've also tried putting the moveToLastComment() function call in viewDidLoad after the reloadData(), viewWillAppear and viewDidAppear. No change. In fact, when I put it anywhere other than viewDidAppear, absolutely nothing happens. I have to scroll down as usual

adjuremods
  • 2,938
  • 2
  • 12
  • 17
Chrishon Wyllie
  • 209
  • 1
  • 2
  • 11
  • How about putting `moveToLastComment ` in `viewWillAppear`? – mfaani Nov 17 '16 at 19:42
  • Change `scrollToRow` to use `animated: false`? – Craig Siemens Nov 17 '16 at 19:46
  • Hi, thanks for replying. I tried both, no difference. In fact, @Honey, putting it in the viewWillappear does nothing at all. – Chrishon Wyllie Nov 17 '16 at 20:07
  • @ChrishonWyllie It should make a difference, but perhaps not visible to the eye. You get the point right? willAppear happens before the view is coming to the screen. DidAppear happens after. – mfaani Nov 17 '16 at 20:15
  • Sorry, that doesnt work still. I edited my original question. To clarify, I have tried putting it in viewWillAppear, viewDidLoad and viewDidAppear. – Chrishon Wyllie Nov 17 '16 at 20:28
  • Possible duplicate of [How to scroll to the bottom of a UITableView on the iPhone before the view appears](http://stackoverflow.com/questions/2770158/how-to-scroll-to-the-bottom-of-a-uitableview-on-the-iphone-before-the-view-appea) – pableiros Nov 17 '16 at 20:32
  • You need to insert the row at the bottom of the tableview. Detect which cell is displayed at the bottom of the tableview then insert that row – MSU_Bulldog Nov 17 '16 at 20:44

3 Answers3

1

To respond to @shallowThought: Again, implementing viewWillAppear does absolutely nothing to the view. No movement of any kind. The TableViewController loads all the data but remains at the very top. However, I did try implementing viewDidAppear again and viewWillAppear together. I believe viewWillAppear couldn't work alone because you can't reload a tableView when the tableView itself has not "appeared" or "loaded". viewWillAppear seems to work more with fetching data, configuring separator styles, etc. So this is what I did:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    self.navigationController?.setToolbarHidden(true, animated: true)
    self.tableView.separatorStyle = .none
    self.tableView.setNeedsLayout()
    self.tableView.layoutIfNeeded()
    self.tableView.rowHeight = UITableViewAutomaticDimension
    self.tableView.estimatedRowHeight = 80

    NotificationCenter.default.addObserver(self, selector: #selector(SingleChatRoomTableViewController.keyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)

    NotificationCenter.default.addObserver(self, selector: #selector(SingleChatRoomTableViewController.keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)

    DispatchQueue.global().async {
        DataService.dataService.fetchChatRoomCommentsFromServer(chatRoomId: self.chatRoomId) { (snap) in
            self.chatRoomMessages.append(snap)
        }
    }
}


override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    DispatchQueue.main.async {
        self.tableView.reloadData()
        self.moveToLastComment()
    }
}

This works the way I intended; The tableview loads at the very last cell without visibly scrolling to that row or visibly "teleporting" to that row. Whether or not it visibly scrolls or teleports is the difference between specifying true or false in the scrollToRow method call. This way does neither, the view is simply waiting at the very last row without showing the user.

HOWEVER... the actual cells that display the messages are somewhat slow to load. Perhaps this is due to the nature of Firebase or how I'm loading the data. The tableView itself seems to "know" where the last message-row will be, but the actual content messages have to load which is still visible. In any case, that's a topic for another question.

Chrishon Wyllie
  • 209
  • 1
  • 2
  • 11
0

Set animated to false:

self.tableView?.scrollToRow(at: pathToLastRow as IndexPath, at: UITableViewScrollPosition.bottom, animated: false)

Also, delete viewDidLoad and viewDidAppear and implement viewWillAppear instead:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    fetchChatRoomCommentsFromServer(chatRoomId: self.chatRoomId) { (snap) in
        self.chatRoomMessages.append(snap)
        self.tableView.reloadData()
        moveToLastComment()
    }
}
shallowThought
  • 19,212
  • 9
  • 65
  • 112
  • It sorta works but not as intended. It still "scrolls" to the bottom but not in an animated way. I want it load at the bottom immediately. This method just jumps to the bottom – Chrishon Wyllie Nov 17 '16 at 20:08
  • follow Honeys advice. Place it in `viewWillAppear` or `viewDidLoad`(after `reloadData`) – shallowThought Nov 17 '16 at 20:19
  • I've tried her advice, it doesn't work. Before asking the question, I've tried putting it in viewDidLoad, viewWillAppear, viewDidAppear, after reloadData(), etc. The tableView doesn't even move from the very first cell when I put it anywhere other than viewDidAppear – Chrishon Wyllie Nov 17 '16 at 20:26
  • I updated the answer. If the problem persists, describe more detailed what happens "It still "scrolls" to the bottom but not in an animated way." – shallowThought Nov 18 '16 at 09:12
0

I needed similar behavior but went down a different path than you which doesn't require any of the scrolling code. In mine, the most recent messages ("comments") are always inserted at the top of the list. To make them appear at the bottom of the screen, I set the transform of the UITableView:

self.tableView.transform    = CGAffineTransformMakeScale(1, -1);

This makes an "upside down" tableview, which is no good since all the cell content is also upside down. You can fix that with:

- (void) tableView: (UITableView *) tableView willDisplayCell: (UITableViewCell *) cell forRowAtIndexPath:(NSIndexPath *) indexPath
{
    cell.transform = self.tableView.transform;
} 

As long as the user hasn't manually scrolled away, any new incoming messages are inserted nicely, pushing the other messages "up".

TomSwift
  • 39,369
  • 12
  • 121
  • 149