Is there any callback or method to notify when user has scrolled to top of messages to load more messages? I am loading messages from server and need some pagination logic.
@basitsattar You need to add your own UIRefreshControl. You can look at how this is achieved in the example app. We also have a method in MessagesCollectionView to reload data and maintain the current position in the scroll view.
I'm adding a feature request to this issue because it may be good to support something like a "Load More Messages" view that JSQMessagesViewController has. Kind of unsure how I want to approach this though.
You can also add func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) in your MessagesViewController-subclass. In this method you check if the first message is visible, and if this is the case, load more messages.
This is less elegant than what WhatsApp does but a good first solution. A nicer solution would catch scrolling up and load more messages before the user gets to the top of the scroll view.
It would look something like this, but not stable.
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
var visibleRect = CGRect()
visibleRect.origin = self.messagesCollectionView.contentOffset
visibleRect.size = self.messagesCollectionView.bounds.size
let visiblePoint = CGPoint(x: visibleRect.midX, y: visibleRect.midY)
guard let indexPath = self.messagesCollectionView.indexPathForItem(at: visiblePoint) else { return }
print(" \(indexPath)")
if indexPath.section == 3 {
self.loadMessages()
}
}
https://github.com/akveo/kittenTricks
On Thu, Mar 22, 2018 at 6:52 PM, Nik Kov notifications@github.com wrote:
It would look something like this, but not stable.
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
var visibleRect = CGRect()visibleRect.origin = self.messagesCollectionView.contentOffset visibleRect.size = self.messagesCollectionView.bounds.size let visiblePoint = CGPoint(x: visibleRect.midX, y: visibleRect.midY) guard let indexPath = self.messagesCollectionView.indexPathForItem(at: visiblePoint) else { return } print(" \(indexPath)") if indexPath.section == 3 { self.loadMessages() } }—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/MessageKit/MessageKit/issues/441#issuecomment-375313526,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AIHKV2tu6O6zD09lXGk5iTgroEG27mx8ks5tg6y3gaJpZM4RUYq2
.
--
Abdul Basit
National University of Sciences & Technology (Pakistan)
Former Exchange Student at University of Wisconsin, Eau Claire (United
States)
Member of Pak-US Alumni Network
Freelance Web & App Developer at Upwork
https://www.upwork.com/freelancers/~014efacb53dce35b99
basit@ basit@hashlogics.comabdulbasit.info
www.abdulbasit.info
+1 on this feature request.
MessageKit is an awesome tool, far better than what I was using before. However, scrolling up to older messages is much less desirable using the UIRefreshControl. The issue is that the user really has to give it a good yank and sometimes the refresh is not triggered, giving user impression there are no more records.
In all of my other scrolling lists where I have access to tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) I set the numRows to numLoaded+1 and when I get close to the last cell I automatically load more rows and create a very smooth scrolling experience.
If there were a delegate call notifying when the user scrolls to top (or top - N) then we could do the load in the background and the user would not have to yank so much on the UIRefreshControl.
Thanks
KeithB
@tkeithblack you can use the scroll view delegate methods or override cellForRow and return the super
Sent with GitHawk
Thanks for the reply @nathantannar4, but I can't figure out how to override either the UIScrollView or UITableView. I've tried doing so in SmsMessagesViewController: MessagesViewController..., but the compiler tells me there is no override for the scroll functions.
KeithB
Correct, we don't use the UIScrollViewDelegate methods so you don't have to override them. I said overriding cellForRow is available (and by the way MessageKit uses a UICollectionView). I suggest taking a look at the source code as reference if you want to see which methods we use.
@nathantannar4, thanks for your always prompt replies.
KB: Ignore this message, see the next one with my working solution!
Prior to this discussion I had already attempted to override:
collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath)
The issue I ran into was that regardless of how few or how many initial rows I load, every row is enumerated through cellForItemAt upon load, even if not displayed. This sets off a chain reaction. When my code sees the top row (section) it thinks it has to load more.
if messageList.count <= indexPath.section + 1 {
// Don't load if we're in the middle of a load request or if all
// records have already been loaded.
if !isLoadingList && !allRecordsLoaded {
loadMessages(messageList.count + 10)
}
}
The result is that every message in the DB is loaded.
With a standard UITableView you're not called if the row is not in view and therefore this code will load more as the user scrolls.
Please let me know if I'm missing something.
Regards,
KeithB
@nathantannar4 ,
I've got it working. The code above was looking for the Last row, not the First row. So like I said before, the initial load calls cellForItemAt on every cell, but after the initial load the 0 cell is not called again until we scroll to the top. The following code works to smoothly scroll up:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.section < 4 { // Are we within 3 rows from the top?
if !isLoadingList && !allRecordsLoaded {
loadMessages(messageList.count + AdditionalRecordsToLoad)
}
}
return super.collectionView(collectionView, cellForItemAt: indexPath)
}
What occurs is that on the first load this code will trigger a second load, then subsequent loads will only occur if the user scrolls to the top.
Thanks for your help,
KeithB
You should use the UIScrollViewDelegate methods to track where the offset is.
Sent with GitHawk
Will this feature be added? Seems like a core feature to me. Clunky with the scrollview and worse with the refreshControl
@kornha this kind of feature can me achieved with either of the delegates, and since its use case specific it's not general enough to be added to the library. From the frameworks perspective, it has no idea where the messages are stored and cannot fetch them for you.
Sent with GitHawk
`
func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths:
[IndexPath]) {
if let first = indexPaths.first, first.section <= 4 {
DispatchQueue.main.async {
self.chatDataSource.fetchOlderMessages()
}
}
}
`
In my case I did use scrollViewDidScroll method:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
print("y: \(scrollView.contentOffset.y)")
if scrollView.contentOffset.y <= 0{
print("in top top top")
}
}
override func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if indexPath.section == 2 {
print("Load more messages")
}
}
messagesCollectionView.prefetchDataSource = self
And create function:
extension VC_Chat: UICollectionViewDataSourcePrefetching {
func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) {
if let last = indexPaths.last, messages.count - 1 == last.section {
print("Load more messages from PREFETCH")
}
}
}
Most helpful comment
It would look something like this, but not stable.