8
votes

Any ideas how to resolve the fact my UICollectionView crashing when scrolling whist rotating?

I am using the following approach for scrolling and rotation separately, and each seem to work fine. I've just noted when doing both things at the same time I can get this crash. So it seems to be something to do with the fact when I rotate the device and the new layout attributes are being calculated within prepareLayout, that the continual scrolling is triggering "invalidateLayoutWithContext(invalidContext)" (see below).

Ideas? Is there a way to put any scrolling responses on-hold (or ignore them) during the rotation?

Rotation Approach In viewWillLayoutSubviews in the view controller I invalidate the whole layout

self.cal.collectionViewLayout.invalidateLayout()

Scrolling Approach To allow me to have a "sticky" decorative view (header) I don't invalidate the whole layout, as it kills performance, but do the following. In the layout class I override shouldInvalidateLayoutForBoundsChange

    override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {
        let invalidContext : UICollectionViewLayoutInvalidationContext = self.invalidationContextForBoundsChange(newBounds)

        // Keep Header Sticky
invalidContext.invalidateDecorationElementsOfKind(GCCalendarLayoutKind_Decorative1, atIndexPaths: [headerDecorativeIndexPath])

        // Apply Invalidation
        self.invalidateLayoutWithContext(invalidContext)        

        // Return normal super return (just in case of future IOS upgrades)
        return super.shouldInvalidateLayoutForBoundsChange(newBounds)
    }

Note here I'm invalidating the Decorative View (header), whereas the error with the crash is about my supplementary view layouts being different.

Error

2015-10-30 07:14:30.181 test3_collectionview[17086:3102132] * Assertion failure in -[UICollectionViewData validateLayoutInRect:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3512.29.5/UICollectionViewData.m:408 2015-10-30 07:14:30.185 test3_collectionview[17086:3102132] * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'layout attributes for supplementary item at index path ( {length = 2, path = 0 - 0}) changed from index path: ( {length = 2, path = 0 - 0}); element kind: (Decorative1); frame = (0 1085.5; 320 16); zIndex = 1; to index path: ( {length = 2, path = 0 - 0}); element kind: (Decorative1); frame = (0 853.5; 320 16); zIndex = 1; without invalidating the layout' *** First throw call stack:

1
If you don't override the shouldInvalidateLayoutForBoundsChange method, does the crash continue to happen ?Lefteris
@Lefteris the crash doesn't happen in this case, however I'm not then sure how to achieve what I'm after without impacting collectionView performance (i.e. not sure then how to use the invalidateLayoutWithContext approach)Greg

1 Answers

1
votes

I am not sure this would be enough but I would give it a try something along these lines (a collection view is a scroll view):

import CoreGraphics

class myController: UIViewController, UIScrollViewDelegate {

    var myScrollView = UIScrollView()

    override func viewDidLoad() {
        self.view.addSubview(myScrollView)
    }

    // This will capture rotation events
    override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
        self.myScrollView.scrollEnabled = false
        coordinator.notifyWhenInteractionEndsUsingBlock( {_ in self.myScrollView.scrollEnabled = true} )
        super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
    }

    func scrollViewDidScroll(scrollView: UIScrollView) {
        // This will not ignore scroll but perhaps can help keeping things "tidy" and "performant" during rotation.  Not sure
        if myScrollView.scrollEnabled == false {
            let offset = scrollView.contentOffset
            myScrollView.setContentOffset(offset, animated: false)
        }
    }   
}