Reputation: 53112
I have a method that I use to scroll to the bottom of my collectionView
- (void) scrollToBottom {
if (_messagesArray.count > 0) {
static NSInteger section = 0;
NSInteger item = [self collectionView:_myCollectionView numberOfItemsInSection:section] - 1;
if (item < 0) item = 0;
NSIndexPath *lastIndexPath = [NSIndexPath indexPathForItem:item inSection:section];
[_myCollectionView scrollToItemAtIndexPath:lastIndexPath atScrollPosition:UICollectionViewScrollPositionBottom animated:YES];
}
}
And it works perfectly! I have never had a problem at any point, it works 100% consistent . . . with a modal view presentation. If however, I push the viewController onto a navigation controller, it doesn't work. Rather, it only works if there's over 15 cells. Once I reach at least 15 cells, it again begins behaving perfectly. Prior to 15 cells, it either doesn't scroll at all, or scrolls a little bit.
I realize this is a long shot, but I'm scratching my head on this one and I thought maybe somebody might know what the heck is happening.
Troubleshoots:
Have you logged to make sure it's running? YES
Have you logged index path to make sure its trying to scroll to the correct index path? YES
Upvotes: 5
Views: 7618
Reputation: 1435
I found nice solution here
- (void)scrollToBottomAnimated:(BOOL)animated
{
if ([self.collectionView numberOfSections] == 0) {
return;
}
NSInteger items = [self.collectionView numberOfItemsInSection:0];
if (items == 0) {
return;
}
CGFloat collectionViewContentHeight = [self.collectionView.collectionViewLayout collectionViewContentSize].height;
BOOL isContentTooSmall = (collectionViewContentHeight < CGRectGetHeight(self.collectionView.bounds));
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
[self.collectionView scrollRectToVisible:CGRectMake(0.0, collectionViewContentHeight - 1.0f, 1.0f, 1.0f)
animated:animated];
return;
}
// workaround for really long messages not scrolling
// if last message is too long, use scroll position bottom for better appearance, else use top
// possibly a UIKit bug, see #480 on GitHub
NSUInteger finalRow = MAX(0, [self.collectionView numberOfItemsInSection:0] - 1);
NSIndexPath *finalIndexPath = [NSIndexPath indexPathForItem:finalRow inSection:0];
CGSize finalCellSize = [self.collectionView.collectionViewLayout sizeForItemAtIndexPath:finalIndexPath];
CGFloat maxHeightForVisibleMessage = CGRectGetHeight(self.collectionView.bounds) - self.collectionView.contentInset.top - CGRectGetHeight(self.inputToolbar.bounds);
UICollectionViewScrollPosition scrollPosition = (finalCellSize.height > maxHeightForVisibleMessage) ? UICollectionViewScrollPositionBottom : UICollectionViewScrollPositionTop;
[self.collectionView scrollToItemAtIndexPath:finalIndexPath
atScrollPosition:scrollPosition
animated:animated];
}
Upvotes: 0
Reputation: 1081
Running into the same issue in a context where I had access to the cell, I was able to use scrollRectToVisible
which doesn't seem to exhibit this behavior.
Upvotes: 1
Reputation: 6694
There is a automaticallyAdjustsScrollViewInsets
property in UIViewController
, and as it's inherited, in UINavigationController
. Set it to NO
in self.navigationController
and it won't adjust your insets, as documented here
Upvotes: 5
Reputation: 53112
I found the problem.
The NavigationController was automatically adjusting my top content inset for some reason. I was able to prevent this behavior by adding this:
- (void) scrollToBottom {
if (_isNavigationControllerVersion) {
_myCollectionView.contentInset = UIEdgeInsetsZero;
}
if (_messagesArray.count > 0) {
static NSInteger section = 0;
NSInteger item = [self collectionView:_myCollectionView numberOfItemsInSection:section] - 1;
if (item < 0) item = 0;
NSIndexPath *lastIndexPath = [NSIndexPath indexPathForItem:item inSection:section];
[_myCollectionView scrollToItemAtIndexPath:lastIndexPath atScrollPosition:UICollectionViewScrollPositionBottom animated:YES];
}
}
Upvotes: 2
Reputation: 62052
So, since your .gif demonstrates that the problem is happening when the keyboard is open, I can only assume that the bottom scroll is within the content inset of the collection view, even if it is behind the keyboard.
Be sure you're resetting the content inset when the keyboard appears/disappears. I will edit in some example code.
First, you need to register for the notifications:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
This should go in viewDidLoad
. Don't forget to removeObserver
in dealloc
.
Now set the content insets in these methods:
- (void)keyboardWasShown:(NSNotification *)aNotification {
CGSize kbSize = [[[aNotification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
UIEdgeInsets insets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
yourCollectionView.contentInset = Insets;
yourCollectionView.scrollIndicatorInsets = Insets;
}
- (void) keyboardWillHide:(NSNotification *)aNotification {
UIEdgeInsets insets = UIEdgeInsetsZero;
yourCollectionView.contentInset = contentInsets;
yourCollectionView.scrollIndicatorInsets = contentInsets;
}
Upvotes: 1