Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issues with dynamic insertion + removal #243

Merged
merged 5 commits into from
May 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions Sources/Pageboy.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
/* Begin PBXBuildFile section */
460906B7222A785E005474BF /* WeakContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 460906B6222A785E005474BF /* WeakContainer.swift */; };
460906B9222A79F6005474BF /* IndexedObjectMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 460906B8222A79F6005474BF /* IndexedObjectMap.swift */; };
461D6DF1201795A100E0CDEE /* UIScrollView+ScrollActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 461D6DF0201795A100E0CDEE /* UIScrollView+ScrollActivity.swift */; };
461D6DF2201795A100E0CDEE /* UIScrollView+ScrollActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 461D6DF0201795A100E0CDEE /* UIScrollView+ScrollActivity.swift */; };
461D6DF1201795A100E0CDEE /* UIScrollView+Interaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 461D6DF0201795A100E0CDEE /* UIScrollView+Interaction.swift */; };
461D6DF2201795A100E0CDEE /* UIScrollView+Interaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 461D6DF0201795A100E0CDEE /* UIScrollView+Interaction.swift */; };
462A65DD2000FCAA0051C79C /* UIView+Localization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 462A65D92000FCAA0051C79C /* UIView+Localization.swift */; };
462A65DE2000FCAA0051C79C /* UIView+Localization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 462A65D92000FCAA0051C79C /* UIView+Localization.swift */; };
462A65DF2000FCAA0051C79C /* UIView+AutoLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 462A65DA2000FCAA0051C79C /* UIView+AutoLayout.swift */; };
Expand Down Expand Up @@ -85,7 +85,7 @@
/* Begin PBXFileReference section */
460906B6222A785E005474BF /* WeakContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeakContainer.swift; sourceTree = "<group>"; };
460906B8222A79F6005474BF /* IndexedObjectMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IndexedObjectMap.swift; sourceTree = "<group>"; };
461D6DF0201795A100E0CDEE /* UIScrollView+ScrollActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIScrollView+ScrollActivity.swift"; sourceTree = "<group>"; };
461D6DF0201795A100E0CDEE /* UIScrollView+Interaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIScrollView+Interaction.swift"; sourceTree = "<group>"; };
462A65D92000FCAA0051C79C /* UIView+Localization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Localization.swift"; sourceTree = "<group>"; };
462A65DA2000FCAA0051C79C /* UIView+AutoLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+AutoLayout.swift"; sourceTree = "<group>"; };
462A65DB2000FCAA0051C79C /* UIApplication+SafeShared.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIApplication+SafeShared.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -160,7 +160,7 @@
462A65DA2000FCAA0051C79C /* UIView+AutoLayout.swift */,
462A65DB2000FCAA0051C79C /* UIApplication+SafeShared.swift */,
462A65DC2000FCAA0051C79C /* UIPageViewController+ScrollView.swift */,
461D6DF0201795A100E0CDEE /* UIScrollView+ScrollActivity.swift */,
461D6DF0201795A100E0CDEE /* UIScrollView+Interaction.swift */,
462D03DE2091D3AB0033C710 /* UIView+Animation.swift */,
BB5277382211308B003695BB /* DispatchQueue+main.swift */,
);
Expand Down Expand Up @@ -502,7 +502,7 @@
462A65E32000FCAA0051C79C /* UIPageViewController+ScrollView.swift in Sources */,
460906B9222A79F6005474BF /* IndexedObjectMap.swift in Sources */,
46ADAAC2208F7E8500974529 /* TransitionOperation.swift in Sources */,
461D6DF1201795A100E0CDEE /* UIScrollView+ScrollActivity.swift in Sources */,
461D6DF1201795A100E0CDEE /* UIScrollView+Interaction.swift in Sources */,
462A65DF2000FCAA0051C79C /* UIView+AutoLayout.swift in Sources */,
46ADAAC8208F7EB200974529 /* PageboyViewController+Updating.swift in Sources */,
BB5277392211308B003695BB /* DispatchQueue+main.swift in Sources */,
Expand Down Expand Up @@ -550,7 +550,7 @@
464ADF562097565D00929AFB /* Page.swift in Sources */,
46ADAAB9208F7E1500974529 /* PageboyViewController+AutoScrolling.swift in Sources */,
462A65E42000FCAA0051C79C /* UIPageViewController+ScrollView.swift in Sources */,
461D6DF2201795A100E0CDEE /* UIScrollView+ScrollActivity.swift in Sources */,
461D6DF2201795A100E0CDEE /* UIScrollView+Interaction.swift in Sources */,
46ADAAC3208F7E8500974529 /* TransitionOperation.swift in Sources */,
462A65E02000FCAA0051C79C /* UIView+AutoLayout.swift in Sources */,
46ADAAC9208F7EB200974529 /* PageboyViewController+Updating.swift in Sources */,
Expand Down
52 changes: 41 additions & 11 deletions Sources/Pageboy/PageboyViewController+Updating.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,27 @@ extension PageboyViewController {
case scrollToUpdate
case scrollTo(index: PageIndex)
}

internal enum UpdateOperation {
case insert
case delete
}
}

// MARK: - Page Updates
internal extension PageboyViewController {

func performUpdates(for newIndex: PageIndex?,
viewController: UIViewController?,
updateBehavior: PageUpdateBehavior,
indexOperation: (_ currentIndex: PageIndex, _ newIndex: PageIndex) -> Void) {
update: (operation: UpdateOperation, behavior: PageUpdateBehavior),
indexOperation: (_ currentIndex: PageIndex, _ newIndex: PageIndex) -> Void,
completion: ((Bool) -> Void)?) {
guard let newIndex = newIndex, let viewController = viewController else { // no view controller - reset
updateViewControllers(to: [UIViewController()],
animated: false,
async: false,
force: false,
completion: nil)
completion: completion)
self.currentIndex = nil
return
}
Expand All @@ -44,33 +50,57 @@ internal extension PageboyViewController {
animated: false,
async: false,
force: false,
completion: nil)
completion: completion)
self.currentIndex = newIndex
return
}

if newIndex == currentIndex { // currently on the page for the update.
// If we are inserting a page that is lower/equal to the current index
// we have to move the current page up therefore we can't just cross-dissolve.
let isInsertionThatRequiresMoving = update.operation == .insert && newIndex <= currentIndex

if !isInsertionThatRequiresMoving && newIndex == currentIndex { // currently on the page for the update.
pageViewController?.view.crossDissolve(during: { [weak self, viewController] in
self?.updateViewControllers(to: [viewController],
animated: false,
async: true,
force: false,
completion: nil)
completion: completion)
})
} else { // update is happening on some other page.
indexOperation(currentIndex, newIndex)

// If we are deleting, check if the new index is greater than the current. If it is then we
// dont need to do anything...
if update.operation == .delete && newIndex > currentIndex {
completion?(true)
return
}

// Reload current view controller in UIPageViewController if insertion index is next/previous page.
if pageIndex(newIndex, isNextTo: currentIndex) {
guard let currentViewController = currentViewController else {
return

let newViewController: UIViewController
switch update.operation {

case .insert:
guard let currentViewController = currentViewController else {
completion?(true)
return
}
newViewController = currentViewController

case .delete:
newViewController = viewController
}

updateViewControllers(to: [currentViewController], animated: false, async: true, force: false, completion: { [weak self, newIndex, updateBehavior] _ in
self?.performScrollUpdate(to: newIndex, behavior: updateBehavior)
updateViewControllers(to: [newViewController], animated: false, async: true, force: false, completion: { [weak self, newIndex, update] _ in
self?.performScrollUpdate(to: newIndex, behavior: update.behavior)
completion?(true)
})
} else { // Otherwise just perform scroll update
performScrollUpdate(to: newIndex, behavior: updateBehavior)
performScrollUpdate(to: newIndex, behavior: update.behavior)
completion?(true)
}
}
}
Expand Down
51 changes: 28 additions & 23 deletions Sources/Pageboy/PageboyViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -276,28 +276,31 @@ open class PageboyViewController: UIViewController {
/// - updateBehavior: Behavior to execute after the page was inserted.
open func insertPage(at index: PageIndex,
then updateBehavior: PageUpdateBehavior = .scrollToUpdate) {
verifyNewPageCount(then: { (oldPageCount, newPageCount) in

verifyNewPageCount(then: { (oldPageCount, newPageCount) in
assert(newPageCount > oldPageCount,
"Attempt to insert page at \(index) but there are only \(newPageCount) pages after the update")
"Attempt to insert page at \(index) but there are only \(newPageCount) pages after the update")

guard let newViewController = dataSource?.viewController(for: self, at: index) else {
assertionFailure("Expected to find inserted UIViewController at page \(index)")
return
}

viewControllerCount = newPageCount
viewControllerIndexMap.removeAll()

performUpdates(for: index,
viewController: newViewController,
updateBehavior: updateBehavior,
indexOperation: { (index, newIndex) in
guard let newViewController = dataSource?.viewController(for: self, at: index) else {
assertionFailure("Expected to find inserted UIViewController at page \(index)")
return
}

if index > newIndex {
currentIndex = index + 1
}
viewControllerCount = newPageCount
viewControllerIndexMap.removeAll()

pageViewController?.scrollView?.cancelTouches()
view.isUserInteractionEnabled = false
performUpdates(for: index, viewController: newViewController,
update: (operation: .insert, behavior: updateBehavior),
indexOperation: { (index, newIndex) in
if index >= newIndex {
currentIndex = index + 1
}},
completion: { (_) in
self.view.isUserInteractionEnabled = true
})
})
})
}

/// Delete an existing page from the page view controller.
Expand Down Expand Up @@ -328,14 +331,16 @@ open class PageboyViewController: UIViewController {
viewControllerCount = newPageCount
viewControllerIndexMap.removeAll()

performUpdates(for: newIndex,
viewController: newViewController,
updateBehavior: updateBehavior,
pageViewController?.scrollView?.cancelTouches()
view.isUserInteractionEnabled = false
performUpdates(for: newIndex, viewController: newViewController,
update: (operation: .delete, behavior: updateBehavior),
indexOperation: { (index, newIndex) in

if index > newIndex {
currentIndex = index - 1
}
}},
completion: { (_) in
self.view.isUserInteractionEnabled = true
})
})
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// UIScrollView+ScrollActivity.swift
// UIScrollView+Interaction.swift
// Pageboy
//
// Created by Merrick Sapsford on 23/01/2018.
Expand All @@ -14,4 +14,9 @@ internal extension UIScrollView {
var isProbablyActiveInScroll: Bool {
return isTracking || isDecelerating
}

func cancelTouches() {
panGestureRecognizer.isEnabled = false
panGestureRecognizer.isEnabled = true
}
}