contentSize ไม่ได้รับการอัพเดตหลังจากเรียกใช้ reloadData บน UICollectionView

ไม่มีใครรู้ว่าเหตุใด contentSize จึงไม่อัปเดตทันทีหลังจากเรียกใช้ reloadData บน UICollectionView

หากคุณต้องการทราบ contentSize งานที่ดีที่สุดที่ฉันพบคือ:

[_collectionView reloadData];

double delayInSeconds = 0.0001;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(DISPATCH_TIME_NOW, dispatch_get_main_queue(), ^(void)
    {
        // TODO: Whatever it is you want to do now that you know the contentSize.
    });

แน่นอนว่านี่เป็นแฮ็กที่ค่อนข้างเปราะบางซึ่งตั้งสมมติฐานเกี่ยวกับการใช้งานของ Apple แต่จนถึงขณะนี้ได้รับการพิสูจน์แล้วว่าทำงานได้ค่อนข้างน่าเชื่อถือ

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

แก้ไข: คำถามนี้ใช้เพื่ออ้างอิงเมธอด setContentOffset ภายในบล็อกเพราะฉันต้องการเลื่อนมุมมองคอลเลกชันในแอปของฉัน ฉันได้ลบการเรียกเมธอดแล้วเนื่องจากคำตอบของผู้คนเน้นไปที่เหตุใดฉันจึงไม่ใช้ scrollToItemAtIndexPath ภายในว่าเหตุใดจึงไม่อัปเดต contentSize


person Reid Main    schedule 30.09.2013    source แหล่งที่มา
comment
ฉันคิดว่าวิธีที่เหมาะสมกว่าคือการใช้ scrollToItemAtIndexPath:atScrollPosition:animated: สำหรับสาเหตุที่ขนาดเนื้อหาไม่อัปเดต ฉันทำได้เพียงสันนิษฐานว่าเป็นการเพิ่มประสิทธิภาพเพื่อป้องกันการเรียกใช้การคำนวณขนาดแบบไดนามิกจำนวนมากและมีราคาแพง หรือส่งคืนข้อมูลที่ไม่ถูกต้อง และโดยการขยายการประมวลผลและการทำงานที่ไม่จำเป็น   -  person Matt    schedule 01.10.2013
comment
ในแอพจริงฉันใช้สิ่งนั้น เพื่อความเรียบง่ายฉันเลือกที่จะพูดถึง setContentOffset นั่นเป็นจุดดีเกี่ยวกับการรีโหลดข้อมูลที่ถูกเรียกหลายครั้งอาจทำให้เกิดการคำนวณที่มีราคาแพง น่าเสียดายที่ไม่มี reloadData พร้อมวิธีจัดการความสมบูรณ์   -  person Reid Main    schedule 01.10.2013
comment
โทร layoutIfNeeded บน UICollectionView ก่อนที่จะเข้าถึง contentSize   -  person onmyway133    schedule 05.03.2020


คำตอบ (6)


หากต้องการรับขนาดเนื้อหาหลังจากโหลดซ้ำ ให้ลองเรียก collectionViewContentSize ของออบเจ็กต์โครงร่าง มันได้ผลสำหรับฉัน

person Clement T    schedule 12.02.2014
comment
ตอนแรกฉันสงสัย แต่มันก็ใช้ได้ผลสำหรับฉันเช่นกัน :) - person Zhang; 09.06.2014
comment
ดูเหมือนว่าการเรียก reloadData จะไม่ทริกเกอร์มุมมองคอลเลกชันทันทีเพื่อเปลี่ยนคุณสมบัติการเรนเดอร์ แต่อย่างที่ Clement แนะนำว่าคุณสามารถไปที่ออบเจ็กต์เลย์เอาต์โดยตรงเพื่อรับข้อมูลนี้ได้ทันที - person Reid Main; 23.05.2015
comment
SWIFT 3: ให้ contentSize = self.myCollectionView.collectionViewLayout.collectionViewContentSize - person drpawelo; 23.10.2017
comment
วิธีนี้ช่วยแก้ไขปัญหาของฉันได้ด้วย :) กำลังเรียกให้ดำเนินการอัปเดตแบบแบตช์เพื่อรับความสูงของเนื้อหาและเกิดปัญหา แต่ปัญหาหลักของฉันคือความสูงของเนื้อหายังไม่มี แต่เลย์เอาต์ของฉันมีความสูงที่เหมาะสม - person Andrew Edwards; 06.12.2018

สิ่งนี้ได้ผลสำหรับฉัน:

[self.collectionView.collectionViewLayout invalidateLayout];
[self.collectionView.collectionViewLayout prepareLayout];
person almas    schedule 20.04.2015
comment
ใช้งานได้สำหรับฉันด้วยการผสมผสานของเลย์เอาต์ถ้าจำเป็น - person Kiran Jasvanee; 13.05.2016
comment
สิ่งนี้ได้ผลสำหรับฉัน ฉันได้รับข้อผิดพลาดนี้จากการปรับขนาดเซลล์ด้วยตนเอง ซึ่ง contentSize นั้นไม่อัปเดตหลังจาก reloadData ใน iOS 10 และก่อนหน้า บน iOS 11 ฉันไม่จำเป็นต้องทำเช่นนี้ - person X.Y.; 12.03.2018
comment
@x.y คุณแก้ไขมันใน iOS 11 ได้อย่างไร มันใช้งานได้หรือเปล่า? - person LinusGeffarth; 27.06.2018

แก้ไข: ฉันเพิ่งทดสอบสิ่งนี้ และเมื่อข้อมูลเปลี่ยนแปลง โซลูชันดั้งเดิมของฉันจะขัดข้องด้วยสิ่งต่อไปนี้:

"การอัปเดตไม่ถูกต้อง: จำนวนรายการไม่ถูกต้องในส่วน 0 จำนวนรายการที่มีอยู่ในส่วนที่มีอยู่หลังการอัปเดต (7) จะต้องเท่ากับจำนวนรายการที่มีอยู่ในส่วนนั้นก่อนการอัปเดต (100) บวกหรือลบ จำนวนรายการที่แทรกหรือลบออกจากส่วนนั้น (แทรก 0 รายการ, ลบ 0 รายการ) และบวกหรือลบจำนวนรายการที่ย้ายเข้าหรือออกจากส่วนนั้น (ย้าย 0 รายการ, ย้ายออก 0 รายการ)

วิธีที่ถูกต้องในการจัดการสิ่งนี้คือการคำนวณการแทรก การลบ และการย้ายเมื่อใดก็ตามที่แหล่งข้อมูลเปลี่ยนแปลง และใช้ PerformBatchUpdates รอบๆ แหล่งข้อมูลเหล่านั้นเมื่อมีการเปลี่ยนแปลง ตัวอย่างเช่น หากมีการเพิ่มสองรายการต่อท้ายอาร์เรย์ซึ่งเป็นแหล่งข้อมูล นี่จะเป็นโค้ด:

NSArray *indexPaths = @[indexPath1, indexPath2];
[self.collectionView performBatchUpdates:^() 
{
    [self.collectionView insertItemsAtIndexPaths:indexPaths];
} completion:^(BOOL finished) {
    // TODO: Whatever it is you want to do now that you know the contentSize.
}];

ด้านล่างนี้เป็นวิธีแก้ปัญหาที่แก้ไขแล้วของคำตอบดั้งเดิมของฉันที่ Kernix ให้ไว้ ซึ่งฉันไม่คิดว่าจะรับประกันว่าจะได้ผล

ลองดำเนินการ BatchUpdates:completion: บน UICollectionView คุณควรมีสิทธิ์เข้าถึงคุณสมบัติที่อัปเดตในบล็อกที่เสร็จสมบูรณ์ มันจะมีลักษณะเช่นนี้:

[self.collectionView reloadData];
[self.collectionView performBatchUpdates:^() 
{

} completion:^(BOOL finished) {
    // TODO: Whatever it is you want to do now that you know the contentSize.
}];
person brodney    schedule 12.10.2013
comment
นั่นดูเหมือนเป็นทางออกที่มั่นคง การเปลี่ยนแปลงจะเคลื่อนไหวซึ่งไม่ใช่สิ่งที่ฉันต้องการ แต่ฉันแน่ใจว่าจะต้องมีวิธีที่จะฆ่าแอนิเมชั่น - person Reid Main; 15.10.2013
comment
คุณสามารถแทนที่แอตทริบิวต์โครงร่างเริ่มต้นในคลาสย่อยโครงร่างได้โดยใช้ InitialLayoutAttributesForAppearingItemAtIndexPath: ตั้งเฟรมให้มีเฟรมของเซลล์ที่เปลี่ยนแปลง - person brodney; 15.10.2013
comment
ฉันค่อนข้างแน่ใจว่าสิ่งนี้จะล้มเหลว คุณควรนำ reloadData ออกจากบล็อก PerformanceBatch - person Aurelien Porte; 29.01.2014

หลังจากโทร

[self.collectionView reloadData];

ใช้สิ่งนี้:

[self.collectionView setNeedsLayout];
[self.collectionView layoutIfNeeded];

แล้วคุณจะได้ของจริง contentSize

person Ben Shabat    schedule 02.06.2021

โทรไปที่preparLayoutก่อนแล้วคุณจะได้เนื้อหาที่ถูกต้องขนาด:

[self.collectionView.collectionViewLayout prepareLayout];
person Robert Mao    schedule 16.05.2014
comment
ไม่ถูกต้อง. การใช้งานเริ่มต้นไม่ได้ทำอะไรเลย ตามที่ Apple ระบุไว้: developer.apple.com/library/prerelease/ios/documentation/UIKit/ - person Can Poyrazoğlu; 08.06.2015
comment
เห็นด้วยกับ @CanPoyrazoğlu - person Max MacLeod; 17.08.2015
comment
สิ่งนี้น่าสนใจ - ฉันมีกรณีที่การเรียก invalidateLayout ตามด้วย reloadData จะไม่ทำให้เกิดการเรียก sizeForItem: อย่างไรก็ตาม การเรียก waitLayout โดยตรงระหว่างการทำให้ไม่ถูกต้องและการโหลดซ้ำจะทำให้ขนาดถูกคำนวณใหม่ระหว่างการโหลดซ้ำอย่างที่ฉันคาดไว้... เอกสารบอกว่าการใช้งานนั้นว่างเปล่า แต่ฉันไม่แน่ใจว่าฉันเชื่อสิ่งนี้โดยสิ้นเชิง... - person tyler; 14.09.2016
comment
@ CanPoyrazoğlu เอกสารบอกว่าคำจำกัดความของวิธีการนั้นว่างเปล่าสำหรับ UICollectionViewLayout ในหลายกรณี คุณจะใช้คลาสย่อยเริ่มต้น UICollectionViewFlowLayout ซึ่งดูเหมือนว่าจะไม่มีการใช้งานที่ว่างเปล่า และเหตุใดจึงใช้งานได้ส่วนใหญ่ - person Jordan Smith; 28.04.2017

ในกรณีของฉัน ฉันมีคลาสโครงร่างแบบกำหนดเองที่สืบทอด UICollectionViewFlowLayout และตั้งค่าไว้ในตัวสร้างอินเทอร์เฟซเท่านั้น ฉันลบคลาสออกจากโปรเจ็กต์โดยไม่ได้ตั้งใจ แต่ Xcode ไม่ได้ให้ข้อผิดพลาดใด ๆ แก่ฉัน และ contentSize ของมุมมองคอลเลกชันส่งคืนศูนย์เสมอ

ฉันนำชั้นเรียนกลับคืนมาและมันก็เริ่มทำงานอีกครั้ง

person da1    schedule 31.10.2019