Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
177 views
in Technique[技术] by (71.8m points)

swift - Retain scrollposition/ behaviour in UITableView after changing the data source

So i have implemented pagination for my Messages UITableView (if the user is about to reach the end of the downloaded messages new ones are fetched and should be displayed as e.g in apps like Instagram). The downloaded messages are sorted in a 2 dimesional array by date so i can display a timestamp in the viewForHeaderInSection. If the local user writes a message i can easily append it to the messages array, sort and display it within reloading the UITableView with reloadData(). So appending to the bottom of the UITableView is working out fine.

Within scrollViewDidScroll i check if the user reaches the end of the downloaded messsages like so:

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    guard !self.loading else {
        return
    }
    guard scrollView.contentOffset.y <= self.tableview.frame.height / 4 else {
        return
    }
    // No Messages, no fetch
    guard let _ = self.messages else {
        return
    }
    self.loading = true
    self.getMessages()
}

Now here comes the problem: After receiving an answer with new messages i sort them with the older messages and append them. Since they are appended to the top of the UITableView, and i cant give a fixed height for any of my cells because the height is dependend on the user input, calling reloadData() results in the UITableView displaying the top of the UITableView instead of staying at the position the reload was called. I know this is intended behaviour because the UITableView does not know about the sizes of the cell and has to layout all of them again (or thats what i think is the reason, correct me if iam wrong).

After researching online i found a solution which fixes the position issue like so (i think this is the way MessageKit implemented it):

public func reloadDataAndKeepOffset() {
    // Stop Scrolling
    //self.setContentOffset(contentOffset, animated: false)
    // Calculate Offset/ Reload
    let beforeContentSize = contentSize
    self.reloadData()
    self.layoutIfNeeded()
    let afterContentSize = contentSize
        
    // Set Offset
    let newOffset = CGPoint(
        x: contentOffset.x + (afterContentSize.width - beforeContentSize.width),
        y: contentOffset.y + (afterContentSize.height - beforeContentSize.height))
    self.setContentOffset(newOffset, animated: false)
}

This works but stops the scroll animation at that certain position. Apps like Instagram also load older messages only if the user request them by scrolling up, but unlike in my app with the current fix the scrolling animation keeps working as the new data is appended and i cant figure out how they did that - any help or suggestions how to do that are welcome.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)
等待大神答复

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...