UICollectionView reloadData ทำงานไม่ถูกต้องใน iOS 7


92

ฉันได้อัปเดตแอปให้ทำงานบน iOS 7 ซึ่งส่วนใหญ่เป็นไปอย่างราบรื่น ฉันสังเกตเห็นในแอพมากกว่าหนึ่งรายการว่าreloadDataวิธีการของ a UICollectionViewControllerไม่ได้แสดงอย่างที่เคยเป็น

ฉันจะโหลดUICollectionViewControllerเติมUICollectionViewข้อมูลตามปกติ ใช้งานได้ดีในครั้งแรก อย่างไรก็ตามหากฉันขอข้อมูลใหม่ (เติมข้อมูลUICollectionViewDataSource) แล้วโทรreloadDataไปข้อมูลนั้นจะค้นหาแหล่งข้อมูลสำหรับnumberOfItemsInSectionและnumberOfSectionsInCollectionViewแต่ดูเหมือนว่าจะไม่เรียกcellForItemAtIndexPathจำนวนครั้งที่เหมาะสม

หากฉันเปลี่ยนรหัสเพื่อโหลดซ้ำเพียงส่วนเดียวมันจะทำงานได้อย่างถูกต้อง นี่ไม่ใช่ปัญหาสำหรับฉันที่จะเปลี่ยนสิ่งเหล่านี้ แต่ฉันไม่คิดว่าฉันควรจะต้องทำ reloadDataควรโหลดเซลล์ที่มองเห็นทั้งหมดซ้ำตามเอกสารประกอบ

มีใครเห็นสิ่งนี้อีกบ้าง?


5
เหมือนกันที่นี่ใน iOS7GM ทำงานได้ดีมาก่อน ฉันสังเกตเห็นว่าการโทรreloadDataหลังจาก viewDidAppear ดูเหมือนจะแก้ปัญหาได้วิธีแก้ปัญหาที่น่ากลัวและต้องการการแก้ไข ฉันหวังว่าจะมีคนช่วยที่นี่
jasonIM

1
มีปัญหาเดียวกัน รหัสที่ใช้ทำงานได้ดีใน iOS6 ตอนนี้ไม่เรียก cellforitematindexpath แม้ว่าจะส่งคืนจำนวนเซลล์ที่เหมาะสม
Avner Barr

สิ่งนี้ได้รับการแก้ไขในรุ่นหลัง 7.0 หรือไม่
William Jockusch

ฉันยังคงประสบปัญหาเกี่ยวกับปัญหานี้
Anil

ปัญหาที่คล้ายกันหลังจากเปลี่ยน [collectionView setFrame] แบบทันที dequeues หนึ่งเซลล์เสมอและนั่นก็คือโดยไม่คำนึงถึงตัวเลขในแหล่งข้อมูล พยายามทุกอย่างที่นี่และอื่น ๆ แต่ไม่สามารถหลีกเลี่ยงได้
RegularExpression

คำตอบ:


72

บังคับสิ่งนี้ในเธรดหลัก:

dispatch_async(dispatch_get_main_queue(), ^ {
    [self.collectionView reloadData];
});

1
ฉันไม่แน่ใจว่าสามารถอธิบายเพิ่มเติมได้หรือไม่ หลังจากค้นหาค้นคว้าทดสอบและตรวจสอบแล้ว ฉันรู้สึกว่านี่เป็นข้อบกพร่องของ iOS 7 การบังคับใช้เธรดหลักจะเรียกใช้ข้อความที่เกี่ยวข้องกับ UIKit ทั้งหมด ดูเหมือนว่าฉันจะเจอปัญหานี้เมื่อเปิดมุมมองจากตัวควบคุมมุมมองอื่น ฉันรีเฟรชข้อมูลบน viewWillAppear ฉันเห็นข้อมูลและการเรียกรีโหลดมุมมองคอลเลคชัน แต่ UI ไม่ได้รับการอัปเดต บังคับเธรดหลัก (เธรด UI) และเริ่มทำงานอย่างน่าอัศจรรย์ เฉพาะใน IOS 7
Shaunti Fondrisi

6
มันไม่สมเหตุสมผลมากนักเพราะคุณไม่สามารถเรียก reloadData จากเธรดหลักได้ (คุณไม่สามารถอัปเดตมุมมองจากเธรดหลักได้) ดังนั้นนี่อาจเป็นผลข้างเคียงที่ทำให้เกิดสิ่งที่คุณต้องการเนื่องจากเงื่อนไขการแข่งขันบางอย่าง
Raphael Oliveira

7
การส่งคิวหลักจากคิวหลักจะทำให้การดำเนินการล่าช้าไปจนถึงการรันลูปถัดไปทำให้ทุกอย่างที่อยู่ในคิวมีโอกาสดำเนินการก่อน
Joony

2
ขอบคุณ !! ยังไม่เข้าใจว่าอาร์กิวเมนต์ Joony ถูกต้องหรือไม่เนื่องจากคำขอข้อมูลหลักใช้เวลาและคำตอบล่าช้าหรือเป็นเพราะฉันกำลังโหลดข้อมูลที่ willDisplayCell
Fidel López

1
ว้าวตลอดเวลาและสิ่งนี้ยังคงเกิดขึ้น นี่เป็นเงื่อนไขการแข่งขันหรือเกี่ยวข้องกับวงจรชีวิตของเหตุการณ์การดู มุมมอง "จะ" ปรากฏขึ้นน่าจะถูกวาดไปแล้ว Joony เข้าใจดีขอบคุณ คิดว่าเราสามารถตั้งค่ารายการนี้เป็น "คำตอบ" ในที่สุดได้หรือไม่?
Shaunti Fondrisi

63

ในกรณีของฉันจำนวนเซลล์ / ส่วนในแหล่งข้อมูลไม่เคยเปลี่ยนแปลงและฉันแค่ต้องการโหลดเนื้อหาที่มองเห็นได้บนหน้าจออีกครั้ง ..

ฉันจัดการเรื่องนี้ได้โดยโทร:

[self.collectionView reloadItemsAtIndexPaths:[self.collectionView indexPathsForVisibleItems]];

แล้ว:

[self.collectionView reloadData];

6
บรรทัดนั้นทำให้แอปของฉันขัดข้อง - "*** การยืนยันล้มเหลวใน - [UICollectionView _endItemAnimations], /SourceCache/UIKit_Sim/UIKit-2935.137/UICollectionView.m:3840"
Lugubrious

@Lugubrious คุณอาจจะแสดงแอนิเมชั่นอื่น ๆ ไปพร้อม ๆ กัน .. ลองใส่ไว้ในperformBatchUpdates:completion:บล็อคดูไหม?
liamnichols

สิ่งนี้ใช้ได้ผลสำหรับฉัน แต่ฉันไม่แน่ใจว่าฉันเข้าใจว่าเหตุใดจึงจำเป็น มีความคิดว่าปัญหาคืออะไร?
Jon Evans

@JonEvans น่าเสียดายที่ฉันไม่รู้ .. ฉันเชื่อว่ามันเป็นข้อบกพร่องบางอย่างใน iOS ไม่แน่ใจว่าได้รับการแก้ไขแล้วในเวอร์ชันที่ใหม่กว่าหรือไม่เนื่องจากฉันยังไม่ได้ทดสอบและโครงการที่ฉันมีปัญหาคือไม่ อีกต่อไปปัญหาของฉัน :)
liamnichols

2
ข้อผิดพลาดนี้เป็นเพียงเรื่องไร้สาระ! เซลล์ทั้งหมดของฉัน - สุ่ม - หายไปเมื่อฉันโหลด collectionView ของฉันใหม่เฉพาะในกรณีที่ฉันมีเซลล์เฉพาะประเภทหนึ่งในคอลเล็กชันของฉัน ฉันหายไปสองวันเพราะฉันไม่เข้าใจว่าเกิดอะไรขึ้นและตอนนี้ฉันใช้วิธีแก้ปัญหาของคุณและมันได้ผลฉันยังไม่เข้าใจว่าทำไมตอนนี้ถึงใช้งานได้ น่าผิดหวังจัง! ยังไงก็ขอบคุณสำหรับความช่วยเหลือ: D !!
CyberDandy

26

ฉันมีปัญหาเดียวกันทุกประการ แต่ฉันก็พบว่าเกิดอะไรขึ้น ในกรณีของฉันฉันเรียกreloadDataจากcollectionView: cellForItemAtIndexPath:ซึ่งดูเหมือนจะไม่ถูกต้อง

การส่งการเรียกreloadDataไปยังคิวหลักช่วยแก้ไขปัญหาครั้งเดียวและตลอดไป

  dispatch_async(dispatch_get_main_queue(), ^{
    [self.collectionView reloadData];
  });

1
คุณสามารถบอกฉันได้ไหมว่าบรรทัดนี้ใช้สำหรับ [self.collectionData.collectionViewLayout invalidateLayout];
iOSDeveloper

สิ่งนี้แก้ไขให้ฉันได้เช่นกัน - ในกรณีของฉันreloadDataถูกเรียกโดยผู้สังเกตการณ์การเปลี่ยนแปลง
sudo ทำการติดตั้ง

นอกจากนี้ยังใช้กับcollectionView(_:willDisplayCell:forItemAtIndexPath:)
Stefan Arambasich

20

การโหลดบางรายการไม่ได้ผลสำหรับฉัน ในกรณีของฉันและเนื่องจาก collectionView ที่ฉันใช้มีเพียงส่วนเดียวฉันจึงโหลดส่วนนั้นซ้ำ คราวนี้มีการโหลดเนื้อหาใหม่อย่างถูกต้อง แปลกที่สิ่งนี้เกิดขึ้นเฉพาะบน iOS 7 (7.0.3)

[self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];

12

ฉันมีปัญหาเดียวกันกับ reloadData บน iOS 7 หลังจากเซสชันดีบักเป็นเวลานานฉันพบปัญหา

บน iOS7 การ reloadData บน UICollectionView จะไม่ยกเลิกการอัปเดตก่อนหน้านี้ซึ่งยังไม่เสร็จสมบูรณ์ (การอัปเดตที่เรียกว่าภายใน performBatchUpdates: block)

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


คุณดำเนินการอัปเดตทั้งหมดภายใน performBatchUpdate ที่ไหน บางในบางส่วนออก? หมดแล้ว? โพสต์ที่น่าสนใจมาก
VaporwareWolf

ฉันใช้มุมมองคอลเลกชันกับ NSFetchedResultsController เพื่อแสดงข้อมูลจาก CoreData เมื่อผู้รับมอบสิทธิ์ NSFetchedResultsController แจ้งการเปลี่ยนแปลงฉันจะรวบรวมการอัปเดตทั้งหมดและเรียกใช้ภายใน performBatchUpdates เมื่อเพรดิเคตคำขอ NSFetchedResultsController มีการเปลี่ยนแปลงต้องเรียก reloadData
user2459624

นี่เป็นคำตอบที่ดีสำหรับคำถามนี้ หากคุณเรียกใช้ reloadItems () (ซึ่งเป็นภาพเคลื่อนไหว) แล้ว reloadData () มันจะข้ามเซลล์
ชีว

12

สวิฟต์ 5 - 4 - 3

// GCD    
DispatchQueue.main.async(execute: collectionView.reloadData)

// Operation
OperationQueue.main.addOperation(collectionView.reloadData)

สวิฟต์ 2

// Operation
NSOperationQueue.mainQueue().addOperationWithBlock(collectionView.reloadData)

4

ฉันก็มีปัญหานี้เช่นกัน โดยบังเอิญฉันได้เพิ่มปุ่มที่ด้านบนของคอลเลกชันวิวเพื่อบังคับให้โหลดซ้ำสำหรับการทดสอบ - และทันใดนั้นวิธีการทั้งหมดก็เริ่มถูกเรียก

นอกจากนี้เพียงแค่เพิ่มบางอย่างง่ายๆ

UIView *aView = [UIView new];
[collectionView addSubView:aView];

จะทำให้เมธอดถูกเรียกใช้

ฉันเล่นกับขนาดเฟรมด้วย - และก็มีการเรียกวิธีการต่างๆ

มีบั๊กมากมายกับ iOS7 UICollectionView


ฉันดีใจที่เห็น (ในรูปแบบของผู้รับรอง) ว่าคนอื่น ๆ ก็ประสบปัญหานี้เช่นกัน ขอบคุณสำหรับวิธีแก้ปัญหา
VaporwareWolf

3

คุณสามารถใช้วิธีนี้

[collectionView reloadItemsAtIndexPaths:arayOfAllIndexPaths];

คุณสามารถเพิ่มindexPathวัตถุทั้งหมดของคุณUICollectionViewลงในอาร์เรย์ได้arrayOfAllIndexPathsโดยการวนซ้ำสำหรับส่วนและแถวทั้งหมดโดยใช้วิธีการด้านล่าง

[aray addObject:[NSIndexPath indexPathForItem:j inSection:i]];

ฉันหวังว่าคุณจะเข้าใจและสามารถแก้ปัญหาของคุณได้ หากคุณต้องการคำอธิบายเพิ่มเติมโปรดตอบกลับ


3

โซลูชันที่ Shaunti Fondrisi มอบให้นั้นเกือบจะสมบูรณ์แบบ แต่โค้ดหรือโค้ดดังกล่าวเช่น enqueue การดำเนินการของUICollectionView's reloadData()to NSOperationQueue' mainQueueทำให้ระยะเวลาดำเนินการไปที่จุดเริ่มต้นของลูปเหตุการณ์ถัดไปในลูปการรันซึ่งอาจทำให้การUICollectionViewอัปเดตด้วยการตวัด

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

CFRunLoopObserver สังเกตกิจกรรมการรอแหล่งอินพุตทั้งหมดและกิจกรรมการเข้าและออกของลูปรัน

public struct CFRunLoopActivity : OptionSetType {
    public init(rawValue: CFOptionFlags)

    public static var Entry: CFRunLoopActivity { get }
    public static var BeforeTimers: CFRunLoopActivity { get }
    public static var BeforeSources: CFRunLoopActivity { get }
    public static var BeforeWaiting: CFRunLoopActivity { get }
    public static var AfterWaiting: CFRunLoopActivity { get }
    public static var Exit: CFRunLoopActivity { get }
    public static var AllActivities: CFRunLoopActivity { get }
}

ในบรรดากิจกรรมเหล่านั้น.AfterWaitingสามารถสังเกตได้เมื่อลูปเหตุการณ์ปัจจุบันกำลังจะสิ้นสุดลงและ.BeforeWaitingสามารถสังเกตได้เมื่อลูปเหตุการณ์ถัดไปเพิ่งเริ่มขึ้น

เนื่องจากมีเพียงNSRunLoopอินสแตนซ์เดียวต่อNSThreadและNSRunLoopขับเคลื่อนNSThreadเท่านั้นเราจึงสามารถพิจารณาได้ว่าการเข้าถึงนั้นมาจากสิ่งเดียวกันNSRunLoopอินสแตนซ์เสมอไม่เคยข้ามเธรด

จากจุดที่กล่าวไว้ก่อนหน้านี้ตอนนี้เราสามารถเขียนโค้ดได้แล้ว: ตัวเลือกจ่ายงานที่ใช้ NSRunLoop:

import Foundation
import ObjectiveC

public struct Weak<T: AnyObject>: Hashable {
    private weak var _value: T?
    public weak var value: T? { return _value }
    public init(_ aValue: T) { _value = aValue }

    public var hashValue: Int {
        guard let value = self.value else { return 0 }
        return ObjectIdentifier(value).hashValue
    }
}

public func ==<T: AnyObject where T: Equatable>(lhs: Weak<T>, rhs: Weak<T>)
    -> Bool
{
    return lhs.value == rhs.value
}

public func ==<T: AnyObject>(lhs: Weak<T>, rhs: Weak<T>) -> Bool {
    return lhs.value === rhs.value
}

public func ===<T: AnyObject>(lhs: Weak<T>, rhs: Weak<T>) -> Bool {
    return lhs.value === rhs.value
}

private var dispatchObserverKey =
"com.WeZZard.Nest.NSRunLoop.TaskDispatcher.DispatchObserver"

private var taskQueueKey =
"com.WeZZard.Nest.NSRunLoop.TaskDispatcher.TaskQueue"

private var taskAmendQueueKey =
"com.WeZZard.Nest.NSRunLoop.TaskDispatcher.TaskAmendQueue"

private typealias DeallocFunctionPointer =
    @convention(c) (Unmanaged<NSRunLoop>, Selector) -> Void

private var original_dealloc_imp: IMP?

private let swizzled_dealloc_imp: DeallocFunctionPointer = {
    (aSelf: Unmanaged<NSRunLoop>,
    aSelector: Selector)
    -> Void in

    let unretainedSelf = aSelf.takeUnretainedValue()

    if unretainedSelf.isDispatchObserverLoaded {
        let observer = unretainedSelf.dispatchObserver
        CFRunLoopObserverInvalidate(observer)
    }

    if let original_dealloc_imp = original_dealloc_imp {
        let originalDealloc = unsafeBitCast(original_dealloc_imp,
            DeallocFunctionPointer.self)
        originalDealloc(aSelf, aSelector)
    } else {
        fatalError("The original implementation of dealloc for NSRunLoop cannot be found!")
    }
}

public enum NSRunLoopTaskInvokeTiming: Int {
    case NextLoopBegan
    case CurrentLoopEnded
    case Idle
}

extension NSRunLoop {

    public func perform(closure: ()->Void) -> Task {
        objc_sync_enter(self)
        loadDispatchObserverIfNeeded()
        let task = Task(self, closure)
        taskQueue.append(task)
        objc_sync_exit(self)
        return task
    }

    public override class func initialize() {
        super.initialize()

        struct Static {
            static var token: dispatch_once_t = 0
        }
        // make sure this isn't a subclass
        if self !== NSRunLoop.self {
            return
        }

        dispatch_once(&Static.token) {
            let selectorDealloc: Selector = "dealloc"
            original_dealloc_imp =
                class_getMethodImplementation(self, selectorDealloc)

            let swizzled_dealloc = unsafeBitCast(swizzled_dealloc_imp, IMP.self)

            class_replaceMethod(self, selectorDealloc, swizzled_dealloc, "@:")
        }
    }

    public final class Task {
        private let weakRunLoop: Weak<NSRunLoop>

        private var _invokeTiming: NSRunLoopTaskInvokeTiming
        private var invokeTiming: NSRunLoopTaskInvokeTiming {
            var theInvokeTiming: NSRunLoopTaskInvokeTiming = .NextLoopBegan
            guard let amendQueue = weakRunLoop.value?.taskAmendQueue else {
                fatalError("Accessing a dealloced run loop")
            }
            dispatch_sync(amendQueue) { () -> Void in
                theInvokeTiming = self._invokeTiming
            }
            return theInvokeTiming
        }

        private var _modes: NSRunLoopMode
        private var modes: NSRunLoopMode {
            var theModes: NSRunLoopMode = []
            guard let amendQueue = weakRunLoop.value?.taskAmendQueue else {
                fatalError("Accessing a dealloced run loop")
            }
            dispatch_sync(amendQueue) { () -> Void in
                theModes = self._modes
            }
            return theModes
        }

        private let closure: () -> Void

        private init(_ runLoop: NSRunLoop, _ aClosure: () -> Void) {
            weakRunLoop = Weak<NSRunLoop>(runLoop)
            _invokeTiming = .NextLoopBegan
            _modes = .defaultMode
            closure = aClosure
        }

        public func forModes(modes: NSRunLoopMode) -> Task {
            if let amendQueue = weakRunLoop.value?.taskAmendQueue {
                dispatch_async(amendQueue) { [weak self] () -> Void in
                    self?._modes = modes
                }
            }
            return self
        }

        public func when(invokeTiming: NSRunLoopTaskInvokeTiming) -> Task {
            if let amendQueue = weakRunLoop.value?.taskAmendQueue {
                dispatch_async(amendQueue) { [weak self] () -> Void in
                    self?._invokeTiming = invokeTiming
                }
            }
            return self
        }
    }

    private var isDispatchObserverLoaded: Bool {
        return objc_getAssociatedObject(self, &dispatchObserverKey) !== nil
    }

    private func loadDispatchObserverIfNeeded() {
        if !isDispatchObserverLoaded {
            let invokeTimings: [NSRunLoopTaskInvokeTiming] =
            [.CurrentLoopEnded, .NextLoopBegan, .Idle]

            let activities =
            CFRunLoopActivity(invokeTimings.map{ CFRunLoopActivity($0) })

            let observer = CFRunLoopObserverCreateWithHandler(
                kCFAllocatorDefault,
                activities.rawValue,
                true, 0,
                handleRunLoopActivityWithObserver)

            CFRunLoopAddObserver(getCFRunLoop(),
                observer,
                kCFRunLoopCommonModes)

            let wrappedObserver = NSAssociated<CFRunLoopObserver>(observer)

            objc_setAssociatedObject(self,
                &dispatchObserverKey,
                wrappedObserver,
                .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }

    private var dispatchObserver: CFRunLoopObserver {
        loadDispatchObserverIfNeeded()
        return (objc_getAssociatedObject(self, &dispatchObserverKey)
            as! NSAssociated<CFRunLoopObserver>)
            .value
    }

    private var taskQueue: [Task] {
        get {
            if let taskQueue = objc_getAssociatedObject(self,
                &taskQueueKey)
                as? [Task]
            {
                return taskQueue
            } else {
                let initialValue = [Task]()

                objc_setAssociatedObject(self,
                    &taskQueueKey,
                    initialValue,
                    .OBJC_ASSOCIATION_RETAIN_NONATOMIC)

                return initialValue
            }
        }
        set {
            objc_setAssociatedObject(self,
                &taskQueueKey,
                newValue,
                .OBJC_ASSOCIATION_RETAIN_NONATOMIC)

        }
    }

    private var taskAmendQueue: dispatch_queue_t {
        if let taskQueue = objc_getAssociatedObject(self,
            &taskAmendQueueKey)
            as? dispatch_queue_t
        {
            return taskQueue
        } else {
            let initialValue =
            dispatch_queue_create(
                "com.WeZZard.Nest.NSRunLoop.TaskDispatcher.TaskAmendQueue",
                DISPATCH_QUEUE_SERIAL)

            objc_setAssociatedObject(self,
                &taskAmendQueueKey,
                initialValue,
                .OBJC_ASSOCIATION_RETAIN_NONATOMIC)

            return initialValue
        }
    }

    private func handleRunLoopActivityWithObserver(observer: CFRunLoopObserver!,
        activity: CFRunLoopActivity)
        -> Void
    {
        var removedIndices = [Int]()

        let runLoopMode: NSRunLoopMode = currentRunLoopMode

        for (index, eachTask) in taskQueue.enumerate() {
            let expectedRunLoopModes = eachTask.modes
            let expectedRunLoopActivitiy =
            CFRunLoopActivity(eachTask.invokeTiming)

            let runLoopModesMatches = expectedRunLoopModes.contains(runLoopMode)
                || expectedRunLoopModes.contains(.commonModes)

            let runLoopActivityMatches =
            activity.contains(expectedRunLoopActivitiy)

            if runLoopModesMatches && runLoopActivityMatches {
                eachTask.closure()
                removedIndices.append(index)
            }
        }

        taskQueue.removeIndicesInPlace(removedIndices)
    }
}

extension CFRunLoopActivity {
    private init(_ invokeTiming: NSRunLoopTaskInvokeTiming) {
        switch invokeTiming {
        case .NextLoopBegan:        self = .AfterWaiting
        case .CurrentLoopEnded:     self = .BeforeWaiting
        case .Idle:                 self = .Exit
        }
    }
}

ด้วยรหัสก่อนตอนนี้เราสามารถส่งการดำเนินการของUICollectionView's reloadData()ไปยังจุดสิ้นสุดของวงเหตุการณ์ปัจจุบันด้วยเช่นชิ้นส่วนของรหัสนี้:

NSRunLoop.currentRunLoop().perform({ () -> Void in
     collectionView.reloadData()
    }).when(.CurrentLoopEnded)

ในความเป็นจริงโปรแกรมมอบหมายงานที่ใช้ NSRunLoop ดังกล่าวอยู่ในกรอบงานที่ใช้ส่วนตัวของฉันแล้ว: Nest และนี่คือที่เก็บบน GitHub: https://github.com/WeZZard/Nest



1

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

NSMutableArray *indexPaths = [[NSMutableArray alloc] init]; 
NSIndexPath *indexPath;
for (int i = 0; i < [self.assets count]; i++) {
         indexPath = [NSIndexPath indexPathForItem:i inSection:0];
         [indexPaths addObject:indexPath];
}
[collectionView reloadItemsAtIndexPaths:indexPaths];`

0

มันเกิดขึ้นกับฉันด้วยเช่นกันใน iOS 8.1 sdk แต่ฉันก็ทำให้ถูกต้องเมื่อฉันสังเกตเห็นว่าแม้หลังจากอัปเดตdatasourceวิธีการแล้วnumberOfItemsInSection:ก็ไม่ได้ส่งคืนการนับรายการใหม่ ฉันอัปเดตการนับและทำให้ใช้งานได้


คุณอัปเดตว่านับได้อย่างไร .. วิธีการทั้งหมดข้างต้นล้มเหลวในการทำงานสำหรับฉันใน swift 3
nyxee

0

คุณตั้งค่า UICollectionView.contentInset หรือไม่ ลบ edgeInset ด้านซ้ายและขวาทุกอย่างเรียบร้อยหลังจากที่ฉันลบข้อผิดพลาดยังคงมีอยู่ใน iOS8.3


0

ตรวจสอบว่าแต่ละวิธีของ UICollectionView Delegate ทำในสิ่งที่คุณคาดหวังให้ทำ ตัวอย่างเช่นถ้า

collectionView:layout:sizeForItemAtIndexPath:

ไม่ส่งคืนขนาดที่ถูกต้องการโหลดซ้ำจะไม่ทำงาน ...


0

ลองใช้รหัสนี้

 NSArray * visibleIdx = [self.collectionView indexPathsForVisibleItems];

    if (visibleIdx.count) {
        [self.collectionView reloadItemsAtIndexPaths:visibleIdx];
    }

0

นี่คือวิธีการทำงานสำหรับฉันในSwift 4

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

let cell = campaignsCollection.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! Cell

cell.updateCell()

    // TO UPDATE CELLVIEWS ACCORDINGLY WHEN DATA CHANGES
    DispatchQueue.main.async {
        self.campaignsCollection.reloadData()
    }

    return cell
}

-1
inservif (isInsertHead) {
   [self insertItemsAtIndexPaths:tmpPoolIndex];
   NSArray * visibleIdx = [self indexPathsForVisibleItems];
   if (visibleIdx.count) {
       [self reloadItemsAtIndexPaths:visibleIdx];
   }
}else if (isFirstSyncData) {
    [self reloadData];
}else{
   [self insertItemsAtIndexPaths:tmpPoolIndex];
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.