UICollectionView เลื่อนอัตโนมัติไปยังเซลล์ที่ IndexPath


101

ก่อนโหลดมุมมองคอลเลกชันผู้ใช้ตั้งค่าจำนวนภาพในอาร์เรย์ของมุมมองคอลเลกชัน เซลล์ทั้งหมดไม่พอดีกับหน้าจอ ฉันมี 30 เซลล์และมีเพียง 6 เซลล์บนหน้าจอ

คำถาม: จะเลื่อนไปยังเซลล์ด้วยภาพที่ต้องการโดยอัตโนมัติที่โหลด UICollectionView ได้อย่างไร?

คำตอบ:


90

ฉันพบว่าการเลื่อนเข้าviewWillAppearอาจทำงานได้ไม่น่าเชื่อถือเนื่องจากมุมมองคอลเลกชันยังไม่เสร็จสิ้นการจัดวาง คุณอาจเลื่อนไปที่รายการที่ไม่ถูกต้อง

ฉันยังพบว่าการเลื่อนเข้าviewDidAppearจะทำให้เห็นแฟลชชั่วขณะของมุมมองที่ไม่ได้เลื่อน

และหากคุณเลื่อนทุกครั้งviewDidLayoutSubviewsผู้ใช้จะไม่สามารถเลื่อนด้วยตนเองได้เนื่องจากเค้าโครงคอลเลกชันบางส่วนทำให้เค้าโครงมุมมองย่อยทุกครั้งที่คุณเลื่อน

นี่คือสิ่งที่ฉันพบว่าทำงานได้อย่างน่าเชื่อถือ:

วัตถุประสงค์ 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];
    }
}

รวดเร็ว:

 override func viewWillLayoutSubviews() {

    super.viewWillLayoutSubviews()

      if (!self.initialScrollDone) {

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

}

เคล็ดลับที่ดี ฉันจะเน้นการใช้initialScrollDoneแฟล็กตามที่ LenK ทำเนื่องจากวิธีนี้จะถูกเรียกมากกว่าหนึ่งครั้งและถ้าคุณเรียก scrollToItemAtIndexPath: มากกว่าหนึ่งครั้งดูเหมือนว่าจะแสดงคอลเลคชันดูไม่สามารถเลื่อนได้ (iOS จำลอง iPhone 5 / iOS 8)
maz

1
ใช้งานได้ดีใน iOS8 ควรเป็นคำตอบที่ได้รับการยอมรับ
นิค

5
สิ่งนี้ไม่ได้ผลสำหรับฉันใน iOS 8.3 มีการเรียกก่อนซึ่งยังทำงานเมื่อคุณทำเช่นนี้ภายใน[self.collectionView layoutIfNeeded]; viewDidLoad
Brent Dillingham

1
นี่ควรเป็นคำตอบที่ได้รับการยอมรับอย่างแน่นอน คำตอบข้างต้นให้แฟลชฉันเมื่อเซลล์แรกปรากฏขึ้นจากนั้นอัปเดตด้วยเซลล์ใหม่ของฉันวิธีนี้จะเปลี่ยนไปอย่างราบรื่น
simon_smiley

สิ่งนี้ยังใช้งานได้บน iOS 9 และไม่ทำให้เค้าโครงอัตโนมัติของฉันยุ่ง
Mr. Zystem

168

ใหม่คำตอบที่แก้ไข:

เพิ่มสิ่งนี้ใน viewDidLayoutSubviews

SWIFT

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

โดยปกติ.centerVerticallyเป็นกรณี

ObjC

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

คำตอบเก่าใช้ได้กับ iOS รุ่นเก่า

เพิ่มสิ่งนี้ในviewWillAppear:

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

4
นั่นคือสิ่งที่ฉันพยายามจะใช้ ฉันมีหมายเลขของวัตถุในอาร์เรย์และฉันรู้ชื่อของวัตถุนั้น แต่ไม่รู้จะใส่ indexPath อย่างไรให้ถูกต้อง ... มีความคิดอย่างไร?
AlexanderZ

4
คุณสามารถสร้างindexPath:[NSIndexPath indexPathForItem:numberOfTheObject inSection:sectionNumber]
boweidmann

3
มีข้อผิดพลาดบางอย่าง แต่ฉันได้ใส่รหัสที่กล่าวถึงทั้งหมดไว้ใน viewDidLayoutSubviews (โมฆะ) และตอนนี้ก็ใช้งานได้ดี Thanx Bogdan
AlexanderZ

1
ฉันจะ 'ซูม' เข้าไปในเซลล์และแสดงภาพขยายได้อย่างไร?
fatuhoku

9
สิ่งนี้ใช้ไม่ได้อย่างน่าเชื่อถือบน iOS 7.1 และอาจทำให้มุมมองคอลเลกชันปรากฏเป็นหน้าจอสีดำ ฉันไม่ต้องการรักษาสถานะเช่นเดียวกับโซลูชันใน viewDidLayoutSubviews ด้านล่าง สิ่งที่ฉันทำมีเพียงแค่แสดง [self.view layoutIfNeeded] ก่อนที่จะเรียก scrollToItemAtIndexPath
Daniel Galasko

15

ฉันเห็นด้วยอย่างยิ่งกับคำตอบข้างต้น สิ่งเดียวคือสำหรับฉันการแก้ปัญหาคือการตั้งรหัสใน viewDidAppear

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

เมื่อฉันใช้ viewWillAppear การเลื่อนไม่ทำงาน


2
viewDidAppear มีข้อเสียตรงที่คุณเห็นมุมมองคอลเลกชั่นเคลื่อนไหว / กระพริบมุมมองปรากฏขึ้นแล้ว (ตามชื่อบอกเป็นนัย ๆ ) ... ฉันสงสัยจริงๆว่าทำไม Apple ถึงไม่ออกแบบเคสที่ไม่ธรรมดาแบบนี้ให้ดีกว่าวิธี viewDidLayoutSubviews คือ อึดอัดนิดหน่อย
TheEye

12

สิ่งนี้ดูเหมือนจะใช้ได้ผลสำหรับฉันมันคล้ายกับคำตอบอื่น แต่มีความแตกต่างที่ชัดเจน:

- (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

รวดเร็ว:

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

สวิฟต์ 3

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

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

ตำแหน่งเลื่อน:

UICollectionViewScrollPosition.CenteredHorizontally / UICollectionViewScrollPosition.CenteredVertically

9

คุณสามารถใช้ GCD เพื่อส่งการเลื่อนไปยังการวนซ้ำถัดไปของลูปรันหลักใน viewDidLoad เพื่อให้บรรลุพฤติกรรมนี้ การเลื่อนจะดำเนินการก่อนที่จะแสดงมุมมองคอลเลคชันบนหน้าจอดังนั้นจะไม่มีการกะพริบ

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

จะรับ indexpath ได้อย่างไรเมื่อตำแหน่งเป็น 3?
kemdo

5

ฉันขอแนะนำให้ทำในcollectionView: willDisplay:จากนั้นคุณสามารถมั่นใจได้ว่าผู้รับมอบสิทธิ์มุมมองคอลเลกชันส่งมอบบางสิ่ง นี่คือตัวอย่างของฉัน:

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
ฉันพบว่าการใช้ didEndDisplaying แทน willDisplay เป็นวิธีที่จะทำให้สิ่งนี้ทำงานได้อย่างถูกต้อง willDisplay ก็ใช้ได้ตราบเท่าที่เซลล์อยู่บนหน้าจอ อย่างไรก็ตามหากเซลล์ยังไม่อยู่บนหน้าจอวิธีนี้จะไม่เลื่อนฉันไปที่มัน didEndDisplaying แม้ว่าจะทำงานได้อย่างสมบูรณ์แบบสำหรับกรณีการใช้งานนั้น ขอบคุณคำตอบของคุณที่ทำให้ฉันมาถูกทาง!
C6Silver

2

เป็นอีกทางเลือกหนึ่งที่กล่าวมาข้างต้น โทรหลังจากโหลดข้อมูล:

รวดเร็ว

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

1

คำตอบทั้งหมดที่นี่ - แฮ็ก ฉันพบวิธีที่ดีกว่าในการเลื่อนมุมมองคอลเลคชันที่ใดที่หนึ่งหลังจาก relaodData:
Subclass collection view เลย์เอาท์ที่คุณเคยใช้มาแทนที่วิธีการจัดเตรียมการจัดเตรียมหลังจาก super call ตั้งค่าสิ่งที่จะชดเชยที่คุณต้องการ
เช่นhttps://stackoverflow.com/a/34192787/1400119

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.