Skip to content
This repository has been archived by the owner on Aug 14, 2019. It is now read-only.

Scroll to bottom fails until contentSize is at least a certain height #256

Closed
jessesquires opened this issue Apr 30, 2014 · 37 comments
Closed

Comments

@jessesquires
Copy link
Owner

  1. begin with empty view
  2. send 1-line messages
  3. messages don't auto-scroll once they reach input toolbar
  4. after a few more messages, collectionView scrolls appropriately
@jessesquires
Copy link
Owner Author

From the thread in #172:

About the scrollToBottom issue I find that it seems like in the viewWillAppear the layout isn't correct yet. The frame and the insets of the collectionView are right but it seems that the internal layout is not calculated yet.
If you move [self scrollToBottomAnimated:NO]; from the viewWillAppear to viewDidAppear it works fine.
But, of course, we want that to be in the viewWillAppear so I tried to call at the next runloop and it works. (with dispatch_async or with dispatch_after):

if (self.autoScrollsToMostRecentMessage) {
    dispatch_async(dispatch_get_main_queue(), ^{
        [self scrollToBottomAnimated:NO];
        [self.collectionView.collectionViewLayout invalidateLayout];
    });
}

EDIT: it seems a problem with autolayout: http://stackoverflow.com/questions/19121607/contentoffset-not-updated-on-uicollectionview-if-scrolltoitematindexpath-is-call

@jessesquires
Copy link
Owner Author

More from thread in #172:

Luckily, I've narrowed it down to the following condition: When the contentSize.height is just slightly smaller (~ 472) or slightly larger (~ 592) than the bounds.size.height (568), scrolling fails. Otherwise, it's ok.

Unfortunately, I think this is something we'll just have to live with for now. :(

@jessesquires
Copy link
Owner Author

/cc: @Antairez

@jessesquires
Copy link
Owner Author

This helps, but it is still not great. It adds extra space until the scrollToIndexPath starts.

- (void)scrollToBottomAnimated:(BOOL)animated
{
    if ([self.collectionView numberOfSections] == 0) {
        return;
    }

    if (self.collectionView.contentSize.height < CGRectGetHeight(self.collectionView.bounds)) {

        CGFloat scrollOffset = self.collectionView.contentSize.height - CGRectGetHeight(self.collectionView.bounds);
        CGFloat insetsOffset = self.collectionView.contentInset.top + self.collectionView.contentInset.bottom;

        if (self.collectionView.contentSize.height <= (CGRectGetHeight(self.collectionView.bounds) - insetsOffset)) {
            return;
        }

        CGPoint bottomOffset = CGPointMake(0.0f, scrollOffset + insetsOffset);
        [self.collectionView setContentOffset:bottomOffset animated:animated];
        return;
    }

    NSInteger items = [self.collectionView numberOfItemsInSection:0];
    if (items < 1) {
        return;
    }

    [self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:items - 1 inSection:0]
                                atScrollPosition:UICollectionViewScrollPositionTop
                                        animated:animated];
}

edit: actually, this doesn't generalize well at all. messages of different sizes really start to throw this off.

@aaronsakowski
Copy link

Here is a hacky way that seems to work for me.

- (void)scrollToBottomAnimated:(BOOL)animated
{
    if ([self.collectionView numberOfSections] == 0) {
        return;
    }

    NSInteger items = [self.collectionView numberOfItemsInSection:0];

    if (items > 0) {
        [self.collectionView layoutIfNeeded];
        CGRect scrollRect = CGRectMake(0, self.collectionView.contentSize.height - 1.f, 1.f, 1.f);
        [self.collectionView scrollRectToVisible:scrollRect animated:animated];
    }
}

Have to force the collectionView to layout otherwise contentSize might not compute properly.

@jessesquires
Copy link
Owner Author

@aaronsakowski - hacky indeed, but not too bad! better than my hack above.

i'll do some testing to see how this behaves and merge if it looks good. 👍

i guess this is a UIKit bug. 😦

@aaronsakowski
Copy link

That's my guess. Thankfully the UIScrollView functions seem to work.

@wdcurry
Copy link

wdcurry commented May 6, 2014

thanks @aaronsakowski, indeed your "hack" works at my end too.. i was searching for a while to try and understand this scrolling issue, then finally read this ;) .. thank you.

@jessesquires
Copy link
Owner Author

@aaronsakowski / @wdcurry

this hack starts to get wonky. sending large messages causes the collection view to scroll up way too much.

also, if you begin with an empty collection view. after you send the 2nd message, the layout of the first bubble screws up and gets all weird. :(

@wdcurry
Copy link

wdcurry commented May 7, 2014

Yeah, I've seen a few randomly odd bubble sizes so far. Odd we can't simply ask the collectionview to just scroll down, period :)

@jessesquires
Copy link
Owner Author

^ yes. agreed. :(

@aaronsakowski
Copy link

Interesting. For me, the message bubble sizes are a bit off for long messages but it still scrolls properly. Can you give the steps to reproduce the strange scrolling?

@jessesquires
Copy link
Owner Author

@aaronsakowski

  1. begin with empty collection
  2. send like 10 messages (just 1-liners)
  3. send a large message (8 lines or so)

you should see that it scrolls up at about double the height of the long message.

@aaronsakowski
Copy link

I can't seem to reproduce.

Steps I took:
Pulled down fresh from develop branch. Started with empty collection. Created 10 messages (just single numbers 1-10 for messages) then created a long lorem ipsum message. It's looking like the screenshot below. Also tried manually typing a long message but didn't see the error.

Maybe somehow the collectionview layout is happening before the input toolbar goes back to it's previous size for a large message?

ios simulator screen shot may 6 2014 9 08 57 pm

@etolstoy
Copy link

etolstoy commented Oct 7, 2014

I'm experiencing some similar issues with scrolling:

  1. When I type new messages in an empty chat, it doesn't scroll to bottom when the messages reach the inputTextView. The scroll happens only when I type some more messages and they reach the bottom of the Collection View.
  2. When I open a chat with a few messages in it it doesn't scroll to bottom too and always appears at the top.

p.s. I'm using the latest 6.0 release of the control.

@dereck009
Copy link

Yes I have a project using 5.x currently, and by backporting this fix it works for the 3 days that I've been using it.

It also works in the 6.x demo project (both simulator, and 5S).

@jessesquires
Copy link
Owner Author

Thanks @dereck009 !

As this works for me too, I'm going to re-close this as resolved.

@kross50 - If you can provide a project/code that will reproduce this issue, I'll be happy to re-open and reinvestigate! 👍

@ghazel
Copy link
Contributor

ghazel commented Nov 27, 2014

I'm still seeing this problem when I adjust the contentInset of the collection view.

collectionViewContentHeight: 758
self.collectionView.bounds.size.height: 667 (iPhone 6, so, full height)
contentInset.top + contentInset.bottom: 288

@ghazel
Copy link
Contributor

ghazel commented Nov 27, 2014

Subtracting the inset top+bottom from collectionViewContentHeight seems to work, but that seems really strange to me.

@jessesquires
Copy link
Owner Author

Thanks @ghazel - I'll check this out!

@jessesquires jessesquires modified the milestones: Release 6.1.1, Release 6.0 Dec 7, 2014
@jessesquires jessesquires modified the milestones: Release 6.0, Release 6.1.1 Dec 19, 2014
@jessesquires
Copy link
Owner Author

hey @ghazel - i couldn't reproduce this on develop. then, i realized that it sounds like you are using custom inset values?

is that the case? if so, let's open a separate issue to track this.

@ghazel
Copy link
Contributor

ghazel commented Dec 19, 2014

I do have custom inset values, but I can reproduce it easily in the JSQ Demo on develop without them:

  • add [self scrollToBottomAnimated:NO]; to the bottom of DemoMessagesViewController viewWillAppear
  • comment out [self addPhotoMediaMessage]; in DemoModelData loadFakeMessages

(run in iOS 8 iPhone 6 simulator)

@alexeymetelkinpro
Copy link

Hey guys,

Current solution seems to not working for me - it still not scroll it to bottom when messages reach it.
How to reproduce on Demo:

  1. set empty view.
  2. add messages one by one (I added a button on view for that with method):
 - (IBAction)receiveFakeTapped:(UIButton *)sender {
    NSMutableArray *userIds = [[self.demoData.users allKeys] mutableCopy];
    [userIds removeObject:self.senderId];
    NSString *randomUserId = userIds[arc4random_uniform((int)[userIds count])];

    NSArray *textes = @[@"Short one",
                        @"A bit longer one, two lines on i5s",
                        @"3 lines text on iPhone 5S - takes three lines of some text"
                        ];

    JSQMessage *newMessage = [JSQMessage messageWithSenderId:randomUserId
                                                 displayName:self.demoData.users[randomUserId]
                                                        text:textes[arc4random_uniform((int)[textes count])]];

    [self.demoData.messages addObject:newMessage];
    [self finishReceivingMessageAnimated:YES];
}
  1. See how it goes under input view and further.

Sorces version 7.3.3 (cloned from develop branch)
Xcode 7.3.1
Tested on

  • iPhone 5s Simulator iOS9.3
  • iPhone 6s Simulator iOS9.3,
  • iPhone 6 Simulator iOS8.4,
  • iPhone 5s iOS 9.3

@alexeymetelkinpro
Copy link

alexeymetelkinpro commented Jul 16, 2016

so solution I found is calculate actual content offset until UIKit is able to scroll it by itself. Some kind of mix of @jessesquires original fix and @dereck009 current solution.

In a - (void)scrollToIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animatedmethod of JSQMessagesViewController

CGFloat collectionViewContentHeight = [self.collectionView.collectionViewLayout collectionViewContentSize].height;
CGFloat collectionViewHeight = CGRectGetHeight(self.collectionView.bounds);
BOOL isContentTooSmall = (collectionViewContentHeight < collectionViewHeight + 10.f);

if (isContentTooSmall) {
    //  workaround for the first few messages not scrolling
    //  when the collection view content size is too small, `scrollToItemAtIndexPath:` doesn't work properly
    //  this seems to be a UIKit bug, see #256 on GitHub

    CGFloat contentWindowH = collectionViewHeight - self.collectionView.contentInset.top - self.collectionView.contentInset.bottom;
    CGFloat offsetH = MAX(0.f, collectionViewContentHeight - contentWindowH) - self.collectionView.contentInset.top;
    [self.collectionView setContentOffset:CGPointMake(0.f, offsetH) animated:YES];

    return;
}

See also that I extended ContentTooSmall zone by 10 points:
BOOL isContentTooSmall = (collectionViewContentHeight < collectionViewHeight + 10.f);
For some reasons sometimes (but quite often on these test messages) when there appears first message that is out of ContentTooSmall zone and goes further to scrollToItemAtIndexPath: method, it scrolls to top.

EDIT:
Of course, there no need in self.topContentAdditionalInset in contentWindowH and offsetH calculation because they already should be checked into contentInset

@jessesquires
Copy link
Owner Author

@alexeyhippie - want to submit a PR?

@tunidev
Copy link

tunidev commented Jul 23, 2016

@jessesquires the bug still exist in 7.3.4, just use the demo project and in the demo conversation just conversation = [message6, message7, message8, message] last message is text with one line, just to be able to add new messages line by line and the collection view will not scroll while receiving the first two messages outside the screen, it scroll only with the third message. and @alexeyhippie code seems the fix the issue

@patniemeyer
Copy link

As a workaround I have added a value observer on the collection view's contentSize property and use it to invoke scrollToBottomAnimated after it has been updated. This seems to solve the problem.

@tamoyal
Copy link

tamoyal commented Apr 19, 2017

This issue also still exists for me. Any chance we can get it reopened?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests