8

I have a UICollectionView to display chat messages. At the beginning I load the existing messages to the collectionView and scroll the collectionView down to the last message with this method:

- (void)scrollToLastMessageAnimated:(BOOL)animated;
{
    if (_messages.count == 0) { return; }

    NSUInteger indexOfLastSection = _messagesBySections.count - 1;
    NSInteger indexOfMessageInLastSection = [_messagesBySections[indexOfLastSection] count] - 1;
    NSIndexPath *path = [NSIndexPath indexPathForItem:indexOfMessageInLastSection
                                            inSection:indexOfLastSection];

    [_collectionView scrollToItemAtIndexPath:path
                           atScrollPosition:UICollectionViewScrollPositionCenteredVertically
                                   animated:animated];
}

This only works animated when I call it in the viewDidAppear: method and not in viewWillAppear: method. How can I scroll down without animation?

electronix384128
  • 6,625
  • 11
  • 45
  • 67
  • If horizontal I use this CGFloat startx = self.neWBooksCollectionView.contentSize.width - 320; [self.neWBooksCollectionView scrollRectToVisible:CGRectMake(startx, 0, 320, 1) animated:YES]; – Khaled Annajar Jun 22 '15 at 13:22

6 Answers6

8

Here it is...

NSInteger section = [self.collectionView numberOfSections] - 1;
NSInteger item = [self.collectionView numberOfItemsInSection:section] - 1;
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:item inSection:section];
[self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:(UICollectionViewScrollPositionBottom) animated:YES];
Shaheen Ghiassy
  • 7,397
  • 3
  • 40
  • 40
itsji10dra
  • 4,603
  • 3
  • 39
  • 59
4

I also provided my answer in another question of yours but I am also writing it here, since this answer is being related only with this problem

Solution

To scroll the view at the last index without crashing before view appears, you first need to trigger the reload of your collectionView data. After it has been reloaded call your method to scroll your view.

-(void)viewWillAppear:(BOOL)animated {
    [collectionView reloadData];
    [self scrollToLastMessageAnimated:YES];
}

Update

[collectionView setContentOffset:CGPointMake(0, CGFLOAT_MAX)]
E-Riddie
  • 14,660
  • 7
  • 52
  • 74
  • it is not working :/ No scroll is happening at all, only when I put call the method in viewDidAppear: – electronix384128 Jun 02 '14 at 15:51
  • @BenMarten Define an offset of it before view appears. Answer edited – E-Riddie Jun 02 '14 at 15:54
  • setContentOffset works for me. You just have to make sure you create the right point, depending on your layout. For instance, for horizontal scrolling layouts, you should pass CGPointMake(CGFLOAT_MAX, 0) instead, since you want to affect the x coordinate, no the y. – Edgar Jun 24 '14 at 11:23
3

Swift 3.0 Code :

let index = IndexPath(item: (self.mFetchedResultsControllerVar?.fetchedObjects?.count)! - 1, section: 0)
self.collectionView?.scrollToItem(at: index, at: UICollectionViewScrollPosition.bottom, animated: true)
krish
  • 3,856
  • 2
  • 24
  • 28
0

A more foolproof solution that handles multiple/0 sections/items

extension UICollectionView {
    func scrollToLastItem(at scrollPosition: UICollectionViewScrollPosition = .centeredHorizontally, animated: Bool = true) {
        let lastSection = numberOfSections - 1
        guard lastSection >= 0 else { return }
        let lastItem = numberOfItems(inSection: lastSection) - 1
        guard lastItem >= 0 else { return }
        let lastItemIndexPath = IndexPath(item: lastItem, section: lastSection)
        scrollToItem(at: lastItemIndexPath, at: scrollPosition, animated: animated)
    }
}
xinatanil
  • 1,085
  • 2
  • 13
  • 23
0

This Extension scrolls to the last section that actually has items. Best approach so far.

public extension UICollectionView {
    func scrollToLastItem() {
        scrollToLastItem(animated: true, atScrollPosition: .bottom)
    }

    func scrollToLastItem(animated: Bool, atScrollPosition scrollPosition: UICollectionViewScrollPosition) {
        guard numberOfSections > 0 else {
            return
        }

        var sectionWithItems: SectionInfo?
        for section in Array(0...(numberOfSections - 1)) {
            let itemCount = numberOfItems(inSection: section)
            if itemCount > 0 {
                sectionWithItems = SectionInfo(numberOfItems: itemCount, sectionIndex: section)
            }
        }

        guard let lastSectionWithItems = sectionWithItems else {
            return
        }

        let lastItemIndexPath = IndexPath(row: lastSectionWithItems.numberOfItems - 1, section: lastSectionWithItems.sectionIndex)
        scrollToItem(at: lastItemIndexPath, at: scrollPosition, animated: animated)
    }
}
Sebastian Boldt
  • 5,283
  • 9
  • 52
  • 64
-4

The problem was actually caused by problematic Autolayout constraints, see my other thread for the solution that helped me in the end: https://stackoverflow.com/a/24033650/2302437

Community
  • 1
  • 1
electronix384128
  • 6,625
  • 11
  • 45
  • 67