Parchment is a library used to implement scroll tab bar menu including infinite scroll. When using this library however I encounter a bug in which user cannot tap on menu. So I write this post to explain why it happens and to how to fix this.
The problem
Here is steps and condition to reproduce the problem.
- Implement infinite scroll implement infinite scroll by looping a finite number of
UIViewController - Scroll the menu a distance long enough to active the infinite mechanism.
- Tap on the tab which have the same title with currently selected tap
Problems:
- Menu item does not response and neither does others.
- Moreover when scrolling the bottom content the content flash in a weird way, you have to scroll and tap on the menu many times until it work normally back.
Below is a gif demonstrating the problem:

Cause
After debugging and searching through the library, it turns out the reason lie in the following code:
open func selectViewController(_ viewController: UIViewController, direction: EMPageViewControllerNavigationDirection, animated: Bool, completion: ((_ transitionSuccessful: Bool) -> Void)?) {
if viewController == self.selectedViewController {
return
}
if (direction == .forward) {
self.afterViewController = viewController
self.layoutViews()
self.loadNewAdjoiningViewControllersOnFinish = true
self.scrollForward(animated: animated, completion: completion)
} else if (direction == .reverse) {
self.beforeViewController = viewController
self.layoutViews()
self.loadNewAdjoiningViewControllersOnFinish = true
self.scrollReverse(animated: animated, completion: completion)
}
}
Among lines of code this line plays the main role of the bug:
if viewController == self.selectedViewController {
return
}
It means when we select a view controller which is the same with current selected view controller then the page view controller will do nothing, despite the face that the new selected PagingItem is different from the previous one. Thus 2 components act incorrectly, causing conflict and bug.
Solution
Knowing the cause, to fix this problem we need to make sure all view controller return in infiniteDataSource is different from each other. There are many ways to do, for example, we can create a UIViewController wrapper to its real content:
extension ViewController: PagingViewControllerInfiniteDataSource {
func pagingViewController(_: PagingViewController, viewControllerFor pagingItem: PagingItem) -> UIViewController {
if let item = pagingItem as? PagingIndexItem {
let vcIndex = viewControllerIndex(from: item.index)
let content = viewControllers[vcIndex]
let wrapper: UIViewController = UIViewController()
wrapper.setContentViewController(content)
return wrapper
}
return UIViewController()
}
}
extension UIViewController {
func setContentViewController(_ content: UIViewController) {
content.willMove(toParent: nil)
content.view.removeFromSuperview()
content.didMove(toParent: nil)
view.addSubview(content.view)
content.view.autoPinEdgesToSuperview()
content.willMove(toParent: self)
addChild(content)
content.didMove(toParent: self)
}
}
You can check more detail in this sample. Hope this could be helpful to you.
