XLPagerTabStrip
XLPagerTabStrip copied to clipboard
Crash by Index out of range because cachedCellWidths is not synched to viewControllers
Phenomenon
Crash by Index out of range because cachedCellWidths is not synched to viewControllers
fatal error: Index out of range
(lldb) po cachedCellWidths
▿ Optional<Array<CGFloat>>
▿ some : 6 elements
- 0 : 93.0
- 1 : 70.5
- 2 : 94.5
- 3 : 102.5
- 4 : 165.5
- 5 : 151.5
(lldb) po indexPath
▿ 2 elements
- 0 : 0
- 1 : 6
(lldb) po viewControllers.count
8
Analysis
-
self?.reloadPagerTabStripView() // I called reloadPagerTabStripView from my ButtonBarPagerTabStripViewController subclass
-
open override func reloadPagerTabStripView() { super.reloadPagerTabStripView() // super.reloadPagerTabStripView() is called before cachedCellWidths is set. So, cachedCellWidths.count is different from viewControllers.count ... cachedCellWidths = calculateWidths()
-
// Eventually, following is called first. open func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize { guard let cellWidthValue = cachedCellWidths?[indexPath.row] else { // So, it crashes here.
call stack
#3 0x00000001106ddf4b in ButtonBarPagerTabStripViewController.collectionView(UICollectionView, layout : UICollectionViewLayout, sizeForItemAtIndexPath : IndexPath) -> CGSize
#4 0x00000001106de24c in @objc ButtonBarPagerTabStripViewController.collectionView(UICollectionView, layout : UICollectionViewLayout, sizeForItemAtIndexPath : IndexPath) -> CGSize ()
#5 0x00000001130bfa82 in -[UICollectionViewFlowLayout _getSizingInfosWithExistingSizingDictionary:] ()
#6 0x00000001130c1114 in -[UICollectionViewFlowLayout _fetchItemsInfoForRect:] ()
#7 0x00000001130ba07e in -[UICollectionViewFlowLayout prepareLayout] ()
#8 0x00000001130da215 in -[UICollectionViewData _prepareToLoadData] ()
#9 0x00000001130919c0 in -[UICollectionView _endItemAnimationsWithInvalidationContext:tentativelyForReordering:animator:] ()
#10 0x000000011308e75a in -[UICollectionView _updateRowsAtIndexPaths:updateAction:] ()
#11 0x00000001106dd7b7 in ButtonBarPagerTabStripViewController.cellForItems(at : [IndexPath], reloadIfNotVisible : Bool)
#12 0x00000001106dcfc4 in ButtonBarPagerTabStripViewController.updateIndicator(for : PagerTabStripViewController, fromIndex : Int, toIndex : Int, withProgressPercentage : CGFloat, indexWasChanged : Bool)
#13 0x000000010ea12122 in XXXPagerTabStripController.updateIndicator(for : PagerTabStripViewController, fromIndex : Int, toIndex : Int, withProgressPercentage : CGFloat, indexWasChanged : Bool)
#14 0x00000001106e22a1 in protocol witness for PagerTabStripIsProgressiveDelegate.updateIndicator(for : PagerTabStripViewController, fromIndex : Int, toIndex : Int, withProgressPercentage : CGFloat, indexWasChanged : Bool) -> () in conformance ButtonBarPagerTabStripViewController ()
#15 0x00000001106f40b4 in PagerTabStripViewController.updateContent()
#16 0x00000001106f4c74 in PagerTabStripViewController.reloadPagerTabStripView()
#17 0x00000001106dbdf9 in ButtonBarPagerTabStripViewController.reloadPagerTabStripView()
Environment
- I'm using 7ff9ed755462fe5e4f891868bc7eafd03f8c4355 close to HEAD in master. I believe this problem is not fixed yet. I checked later commit logs.
- It doesn't happen every time. I don't know how I can reproduce this problem yet.
+1
+1
+1
I have the same problem, and my version (8.0.0) have the 7ff9ed7 changes :(
+1
+1
+1
The crash happens when the new viewControllers.count
is greater that the existing viewControllers.count
. The problem is that PagerTabStripViewController
which is the superclass of ButtonBarPagerTabStripViewController
, calls updateContent()
before the new cachedCellWidths
are calculated - the calculation is done in ButtonBarPagerTabStripViewController.reloadPagerTabStripView()
AFTER super.reloadPagerTabStripView()
is called
Here is a fix for the crash, which postpones the call to updateContent()
after everything is in place:
File: ButtonBarPagerTabStripViewController.swift
add a variable:
private var shouldUpdateContent = true
replace reloadPagerTabStripView() with:
open override func reloadPagerTabStripView() {
shouldUpdateContent = false
super.reloadPagerTabStripView()
shouldUpdateContent = true
guard isViewLoaded else { return }
buttonBarView.reloadData()
cachedCellWidths = calculateWidths()
updateContent()
buttonBarView.moveTo(index: currentIndex, animated: false, swipeDirection: .none, pagerScroll: .yes)
}
and add this function:
open override func updateContent() {
if shouldUpdateContent {
super.updateContent()
}
}
Thanks. @nickbit! This took many hours out of my day until I came upon this. Works great!
emm... when using @nickbit 's solution, i crashed in another place:
if !indexPathsToReload.isEmpty {
buttonBarView.reloadItems(at: indexPathsToReload)
}
with the log
Project(74688,0x700007fb1000) malloc: Heap corruption detected, free list is damaged at 0x600001f48000
*** Incorrect guard value: 105553124933760
Project(74688,0x10e4235c0) malloc: Heap corruption detected, free list is damaged at 0x600001f48020
*** Incorrect guard value: 5134468915308744792
Project(74688,0x700007fb1000) malloc: *** set a breakpoint in malloc_error_break to debug
Project(74688,0x10e4235c0) malloc: *** set a breakpoint in malloc_error_break to debug
@aalenliang I am also getting the same crash. Any update?
@dasSoumen
Have you tried call self.buttonBarView.layoutIfNeeded()
before self.reloadPagerTabStripView()
?
getting the same error Heap corruption detected, free list is damaged at reloadItems. if !indexPathsToReload.isEmpty { buttonBarView.reloadItems(at: indexPathsToReload) }
@aalenliang have u fixed it the issue heap corruption
@dasSoumen @ShoaibPathan Sorry for the late reply, i used george-lin/XLPagerTabStrip instead.
I'm still getting this error... I updated this library today with cocoa pods...any help?
Still getting this crash , Has somebody fixed this. Need help in this
For me, it wasn't crashing if I call reloadPagerTabStripView() from the switch of the same page in a different container. but if I call the same method just after page open then It was crashing. Here is the workaround I found to solve this issue temporary:: Just add the delay of 3 seconds.
extension DispatchQueue {
static func background(delay: Double = 0.0, background: (()->Void)? = nil, completion: (() -> Void)? = nil) {
DispatchQueue.global(qos: .background).async {
background?()
if let completion = completion {
DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: {
completion()
})
}
}
}
}
USAGE---> SVProgressHUD.show() DispatchQueue.background(delay: 3.0, completion:{ // do something in main thread after 3 seconds reloadPagerTabStripView() SVProgressHUD.dismiss() })
this project needs to fork
updated on 24 Aug 2020 Still get error at : buttonBarView.reloadItems(at: indexPathsToReload)
any solution guys ?
Is there any solution for this? Will appreciate it
@nickbit I get 'cachedCellWidths' is inaccessible due to 'private' protection level
@minhmera try adding self.buttonBarView.layoutIfNeeded() before updateContent() with @nickbit answer. It seems to resolve the issue