UICollectionView tự động cuộn đến ô tại IndexPath


101

Trước khi tải chế độ xem bộ sưu tập, người dùng đặt số lượng hình ảnh trong mảng chế độ xem bộ sưu tập. Tất cả các ô không vừa trên màn hình. Tôi có 30 ô và chỉ có 6 ô trên màn hình.

Câu hỏi: Làm thế nào để tự động cuộn đến ô có hình ảnh mong muốn khi tải UICollectionView?

Câu trả lời:


90

Tôi nhận thấy rằng việc cuộn vào viewWillAppearcó thể không hoạt động đáng tin cậy vì chế độ xem bộ sưu tập vẫn chưa hoàn thành bố cục của nó; bạn có thể cuộn đến mục sai.

Tôi cũng thấy rằng cuộn trong viewDidAppear sẽ làm hiển thị một khoảnh khắc chớp nhoáng của chế độ xem chưa cuộn.

Và, nếu bạn cuộn qua mọi lần viewDidLayoutSubviews, người dùng sẽ không thể cuộn theo cách thủ công vì một số bố cục bộ sưu tập gây ra bố cục chế độ xem phụ mỗi khi bạn cuộn.

Đây là những gì tôi thấy hoạt động đáng tin cậy:

Mục tiêu C:

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    // If we haven't done the initial scroll, do it once.
    if (!self.initialScrollDone) {
        self.initialScrollDone = YES;

        [self.collectionView scrollToItemAtIndexPath:self.myInitialIndexPath
                                    atScrollPosition:UICollectionViewScrollPositionRight animated:NO];
    }
}

Swift:

 override func viewWillLayoutSubviews() {

    super.viewWillLayoutSubviews()

      if (!self.initialScrollDone) {

         self.initialScrollDone = true
         self.testNameCollectionView.scrollToItem(at:selectedIndexPath, at: .centeredHorizontally, animated: true)
    }

}

Mẹo hay. Tôi sẽ nhấn mạnh bằng cách sử dụng một initialScrollDonecờ như LenK đã làm vì phương thức này sẽ được gọi nhiều lần và nếu bạn gọi scrollToItemAtIndexPath: nhiều lần nó dường như khiến bộ sưu tập không thể cuộn được (trình mô phỏng iOS iPhone 5 / iOS 8)
maz

1
Hoạt động tuyệt vời trong iOS8. Nên là câu trả lời được chấp nhận.
Nick

5
Điều này không hoạt động với tôi trong iOS 8.3. Phải gọi [self.collectionView layoutIfNeeded];trước, điều này cũng hoạt động khi bạn thực hiện điều này bên trong viewDidLoad.
Brent Dillingham

1
Đây chắc chắn phải là câu trả lời được chấp nhận. Câu trả lời ở trên đã giúp tôi chớp mắt khi ô đầu tiên được hiển thị và sau đó được cập nhật với ô mới của tôi, phương pháp này chuyển đổi liền mạch.
simon_smiley

điều này cũng hoạt động trên iOS 9 và cũng không làm rối bố cục tự động của tôi.
Mr. Zystem,

168

Câu trả lời mới, đã chỉnh sửa:

Thêm cái này vào viewDidLayoutSubviews

NHANH

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    let indexPath = IndexPath(item: 12, section: 0)
    self.collectionView.scrollToItem(at: indexPath, at: [.centeredVertically, .centeredHorizontally], animated: true)
}

Thông thường, .centerVerticallylà trường hợp

ObjC

-(void)viewDidLayoutSubviews {
   [super viewDidLayoutSubviews];
    NSIndexPath *indexPath = [NSIndexPath indexPathForItem:12 inSection:0];
   [self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredVertically | UICollectionViewScrollPositionCenteredHorizontally animated:NO];
}

Câu trả lời cũ hoạt động cho iOS cũ hơn

Thêm cái này vào viewWillAppear:

[self.view layoutIfNeeded];
[self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:NO];

4
Đó chính xác là những gì tôi đang cố gắng sử dụng. Tôi có số của đối tượng trong mảng và tôi biết tên của đối tượng đó. Nhưng không biết làm thế nào để đặt indexPath chính xác ... Bất kỳ ý tưởng?
AlexanderZ

4
Bạn có thể dễ dàng tạo indexPath:[NSIndexPath indexPathForItem:numberOfTheObject inSection:sectionNumber]
boweidmann

3
Có một số lỗi. Nhưng sau đó tôi đã đặt tất cả mã được đề cập bên trong (void) viewDidLayoutSubviews {} và bây giờ nó đang hoạt động tốt. Thanx Bogdan.
AlexanderZ

1
Làm cách nào tôi có thể 'phóng to' vào ô và hiển thị nó được phóng to?
fatuhoku

9
Điều này không hoạt động đáng tin cậy trên iOS 7.1 và có thể dẫn đến chế độ xem bộ sưu tập xuất hiện dưới dạng màn hình đen. Tôi cũng không muốn duy trì trạng thái như giải pháp trong viewDidLayoutSubviews bên dưới. Những gì tôi đã làm chỉ đơn giản là thực hiện một [self.view layoutIfNeeded] ngay trước khi cuộc gọi đến scrollToItemAtIndexPath
Daniel Galasko

15

Tôi hoàn toàn đồng ý với câu trả lời trên. Điều duy nhất là đối với tôi, giải pháp được đặt mã trong viewDidAppear

viewDidAppear 
{
    [self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:YES];
}

Khi tôi sử dụng viewWillAppear, việc cuộn không hoạt động.


2
viewDidAppear có nhược điểm là bạn thấy chế độ xem bộ sưu tập di chuyển / nhấp nháy, chế độ xem đã xuất hiện (như tên của nó) ... Tôi thực sự thắc mắc tại sao Apple không thiết kế trường hợp không quá bất thường này theo cách đẹp hơn, cách viewDidLayoutSubviews là một chút khó xử
TheEye

12

điều này dường như hiệu quả với tôi, nó tương tự như một câu trả lời khác nhưng có một số khác biệt rõ ràng:

- (void)viewDidLayoutSubviews
{     
   [self.collectionView layoutIfNeeded];
   NSArray *visibleItems = [self.collectionView indexPathsForVisibleItems];
   NSIndexPath *currentItem = [visibleItems objectAtIndex:0];
   NSIndexPath *nextItem = [NSIndexPath indexPathForItem:someInt inSection:currentItem.section];

   [self.collectionView scrollToItemAtIndexPath:nextItem atScrollPosition:UICollectionViewScrollPositionNone animated:YES];
}

12

Nhanh:

your_CollectionView.scrollToItemAtIndexPath(indexPath, atScrollPosition: UICollectionViewScrollPosition.CenteredHorizontally, animated: true)

Swift 3

let indexPath = IndexPath(row: itemIndex, section: sectionIndex)

collectionView.scrollToItem(at: indexPath, at: UICollectionViewScrollPosition.right, animated: true)

Vị trí cuộn:

UICollectionViewScrollPosition.CenteredHorizontally / UICollectionViewScrollPosition.CenteredVertically

9

Bạn có thể sử dụng GCD để chuyển cuộn sang lần lặp tiếp theo của vòng lặp chạy chính trong viewDidLoad để đạt được hành vi này. Việc cuộn sẽ được thực hiện trước khi chế độ xem bộ sưu tập được hiển thị trên màn hình, vì vậy sẽ không có nhấp nháy.

- (void)viewDidLoad {
    dispatch_async (dispatch_get_main_queue (), ^{
        NSIndexPath *indexPath = YOUR_DESIRED_INDEXPATH;
        [self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
    });
}

làm thế nào để lấy đường dẫn chỉ mục khi vị trí là 3?
kemdo 30/03/18

5

Tôi khuyên bạn nên làm điều đó trong collectionView: willDisplay:Sau đó, bạn có thể chắc chắn rằng người đại diện chế độ xem bộ sưu tập mang lại điều gì đó. Đây là ví dụ của tôi:

func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    /// this is done to set the index on start to 1 to have at index 0 the last image to have a infinity loop
    if !didScrollToSecondIndex {
        self.scrollToItem(at: IndexPath(row: 1, section: 0), at: .left, animated: false)
        didScrollToSecondIndex = true
    }
}

1
Tôi thấy rằng sử dụng didEndDisplaying, thay vì willDisplay là cách để làm cho điều này hoạt động bình thường. willDisplay vẫn ổn miễn là ô đó ở trên màn hình; tuy nhiên, nếu ô đó chưa xuất hiện trên màn hình, phương pháp này đã không cuộn tôi đến nó. didEndDisplaying mặc dù hoạt động hoàn hảo cho trường hợp sử dụng đó. Đánh giá cao câu trả lời của bạn đưa tôi đi đúng đường!
C6Silver 14/09/18

2

Như một sự thay thế cho đã đề cập ở trên. Gọi sau khi tải dữ liệu:

Nhanh

collectionView.reloadData()
collectionView.layoutIfNeeded()
collectionView.selectItem(at: indexPath, animated: true, scrollPosition: .right)

1

Tất cả câu trả lời ở đây - hacks. Tôi đã tìm ra cách tốt hơn để cuộn chế độ xem bộ sưu tập ở đâu đó sau relaodData:
Bố cục chế độ xem bộ sưu tập lớp con mà bạn đã từng sử dụng, ghi đè phương thức chuẩn bị sẵn sàng, sau khi gọi siêu thiết lập những gì bạn cần bù đắp.
ví dụ: https://stackoverflow.com/a/34192787/1400119

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.