From ac60d190405a36853643998b9da00b5fd06fecac Mon Sep 17 00:00:00 2001 From: Merrick Sapsford <merrick@sapsford.tech> Date: Mon, 4 May 2020 08:06:42 +0200 Subject: [PATCH 1/5] Fix insertion below current index --- Sources/Pageboy/PageboyViewController+Updating.swift | 12 +++++++++++- Sources/Pageboy/PageboyViewController.swift | 4 +++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Sources/Pageboy/PageboyViewController+Updating.swift b/Sources/Pageboy/PageboyViewController+Updating.swift index a3e749a3..6abfdb40 100644 --- a/Sources/Pageboy/PageboyViewController+Updating.swift +++ b/Sources/Pageboy/PageboyViewController+Updating.swift @@ -20,6 +20,11 @@ extension PageboyViewController { case scrollToUpdate case scrollTo(index: PageIndex) } + + internal enum UpdateOperation { + case insert + case delete + } } // MARK: - Page Updates @@ -27,6 +32,7 @@ internal extension PageboyViewController { func performUpdates(for newIndex: PageIndex?, viewController: UIViewController?, + operation: UpdateOperation, updateBehavior: PageUpdateBehavior, indexOperation: (_ currentIndex: PageIndex, _ newIndex: PageIndex) -> Void) { guard let newIndex = newIndex, let viewController = viewController else { // no view controller - reset @@ -49,7 +55,11 @@ internal extension PageboyViewController { 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 = 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, diff --git a/Sources/Pageboy/PageboyViewController.swift b/Sources/Pageboy/PageboyViewController.swift index b3189d56..b9f2473a 100755 --- a/Sources/Pageboy/PageboyViewController.swift +++ b/Sources/Pageboy/PageboyViewController.swift @@ -290,10 +290,11 @@ open class PageboyViewController: UIViewController { performUpdates(for: index, viewController: newViewController, + operation: .insert, updateBehavior: updateBehavior, indexOperation: { (index, newIndex) in - if index > newIndex { + if index >= newIndex { currentIndex = index + 1 } }) @@ -330,6 +331,7 @@ open class PageboyViewController: UIViewController { performUpdates(for: newIndex, viewController: newViewController, + operation: .delete, updateBehavior: updateBehavior, indexOperation: { (index, newIndex) in From 5a2237c366564c925475dbcd6051ef35dba84247 Mon Sep 17 00:00:00 2001 From: Merrick Sapsford <merrick@sapsford.tech> Date: Mon, 4 May 2020 08:56:31 +0200 Subject: [PATCH 2/5] Fix deletion of upper index causing page index corruption --- .../PageboyViewController+Updating.swift | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/Sources/Pageboy/PageboyViewController+Updating.swift b/Sources/Pageboy/PageboyViewController+Updating.swift index 6abfdb40..1eeeb7da 100644 --- a/Sources/Pageboy/PageboyViewController+Updating.swift +++ b/Sources/Pageboy/PageboyViewController+Updating.swift @@ -70,13 +70,29 @@ internal extension PageboyViewController { } 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 operation == .delete && newIndex > currentIndex { + 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 operation { + + case .insert: + guard let currentViewController = currentViewController else { + return + } + newViewController = currentViewController + + case .delete: + newViewController = viewController } - updateViewControllers(to: [currentViewController], animated: false, async: true, force: false, completion: { [weak self, newIndex, updateBehavior] _ in + updateViewControllers(to: [newViewController], animated: false, async: true, force: false, completion: { [weak self, newIndex, updateBehavior] _ in self?.performScrollUpdate(to: newIndex, behavior: updateBehavior) }) } else { // Otherwise just perform scroll update From 0830acd64f9decc4fe34fe153646c7f938abc45b Mon Sep 17 00:00:00 2001 From: Merrick Sapsford <merrick@sapsford.tech> Date: Mon, 4 May 2020 09:05:02 +0200 Subject: [PATCH 3/5] Clean up --- .../Pageboy/PageboyViewController+Updating.swift | 15 +++++++-------- Sources/Pageboy/PageboyViewController.swift | 6 ++---- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/Sources/Pageboy/PageboyViewController+Updating.swift b/Sources/Pageboy/PageboyViewController+Updating.swift index 1eeeb7da..a5d69be1 100644 --- a/Sources/Pageboy/PageboyViewController+Updating.swift +++ b/Sources/Pageboy/PageboyViewController+Updating.swift @@ -32,8 +32,7 @@ internal extension PageboyViewController { func performUpdates(for newIndex: PageIndex?, viewController: UIViewController?, - operation: UpdateOperation, - updateBehavior: PageUpdateBehavior, + update: (operation: UpdateOperation, behavior: PageUpdateBehavior), indexOperation: (_ currentIndex: PageIndex, _ newIndex: PageIndex) -> Void) { guard let newIndex = newIndex, let viewController = viewController else { // no view controller - reset updateViewControllers(to: [UIViewController()], @@ -57,7 +56,7 @@ internal extension PageboyViewController { // 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 = operation == .insert && newIndex <= currentIndex + 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 @@ -72,7 +71,7 @@ internal extension PageboyViewController { // 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 operation == .delete && newIndex > currentIndex { + if update.operation == .delete && newIndex > currentIndex { return } @@ -80,7 +79,7 @@ internal extension PageboyViewController { if pageIndex(newIndex, isNextTo: currentIndex) { let newViewController: UIViewController - switch operation { + switch update.operation { case .insert: guard let currentViewController = currentViewController else { @@ -92,11 +91,11 @@ internal extension PageboyViewController { newViewController = viewController } - updateViewControllers(to: [newViewController], 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) }) } else { // Otherwise just perform scroll update - performScrollUpdate(to: newIndex, behavior: updateBehavior) + performScrollUpdate(to: newIndex, behavior: update.behavior) } } } diff --git a/Sources/Pageboy/PageboyViewController.swift b/Sources/Pageboy/PageboyViewController.swift index b9f2473a..44bd3c11 100755 --- a/Sources/Pageboy/PageboyViewController.swift +++ b/Sources/Pageboy/PageboyViewController.swift @@ -290,8 +290,7 @@ open class PageboyViewController: UIViewController { performUpdates(for: index, viewController: newViewController, - operation: .insert, - updateBehavior: updateBehavior, + update: (operation: .insert, behavior: updateBehavior), indexOperation: { (index, newIndex) in if index >= newIndex { @@ -331,8 +330,7 @@ open class PageboyViewController: UIViewController { performUpdates(for: newIndex, viewController: newViewController, - operation: .delete, - updateBehavior: updateBehavior, + update: (operation: .delete, behavior: updateBehavior), indexOperation: { (index, newIndex) in if index > newIndex { From c32a8e5db56c192db585cb44930d5cc760a57d25 Mon Sep 17 00:00:00 2001 From: Merrick Sapsford <merrick@sapsford.tech> Date: Mon, 4 May 2020 09:07:08 +0200 Subject: [PATCH 4/5] Fix indentation --- Sources/Pageboy/PageboyViewController.swift | 33 +++++++++++---------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/Sources/Pageboy/PageboyViewController.swift b/Sources/Pageboy/PageboyViewController.swift index 44bd3c11..3e38db6b 100755 --- a/Sources/Pageboy/PageboyViewController.swift +++ b/Sources/Pageboy/PageboyViewController.swift @@ -276,28 +276,29 @@ 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 - } + guard let newViewController = dataSource?.viewController(for: self, at: index) else { + assertionFailure("Expected to find inserted UIViewController at page \(index)") + return + } - viewControllerCount = newPageCount - viewControllerIndexMap.removeAll() + viewControllerCount = newPageCount + viewControllerIndexMap.removeAll() - performUpdates(for: index, - viewController: newViewController, - update: (operation: .insert, behavior: updateBehavior), - indexOperation: { (index, newIndex) in + performUpdates(for: index, + viewController: newViewController, + update: (operation: .insert, behavior: updateBehavior), + indexOperation: { (index, newIndex) in - if index >= newIndex { - currentIndex = index + 1 - } + if index >= newIndex { + currentIndex = index + 1 + } + }) }) - }) } /// Delete an existing page from the page view controller. From 0847daef1232f542b5678cf64d12ef37a9c44649 Mon Sep 17 00:00:00 2001 From: Merrick Sapsford <merrick@sapsford.tech> Date: Mon, 4 May 2020 09:15:48 +0200 Subject: [PATCH 5/5] Fix interaction issues when dynamically updating --- Sources/Pageboy.xcodeproj/project.pbxproj | 12 ++++----- .../PageboyViewController+Updating.swift | 13 +++++++--- Sources/Pageboy/PageboyViewController.swift | 26 +++++++++++-------- ...y.swift => UIScrollView+Interaction.swift} | 7 ++++- 4 files changed, 36 insertions(+), 22 deletions(-) rename Sources/Pageboy/Utilities/Extensions/{UIScrollView+ScrollActivity.swift => UIScrollView+Interaction.swift} (67%) diff --git a/Sources/Pageboy.xcodeproj/project.pbxproj b/Sources/Pageboy.xcodeproj/project.pbxproj index 005aef14..378435f2 100755 --- a/Sources/Pageboy.xcodeproj/project.pbxproj +++ b/Sources/Pageboy.xcodeproj/project.pbxproj @@ -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 */; }; @@ -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>"; }; @@ -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 */, ); @@ -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 */, @@ -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 */, diff --git a/Sources/Pageboy/PageboyViewController+Updating.swift b/Sources/Pageboy/PageboyViewController+Updating.swift index a5d69be1..d7b91353 100644 --- a/Sources/Pageboy/PageboyViewController+Updating.swift +++ b/Sources/Pageboy/PageboyViewController+Updating.swift @@ -33,13 +33,14 @@ internal extension PageboyViewController { func performUpdates(for newIndex: PageIndex?, viewController: UIViewController?, update: (operation: UpdateOperation, behavior: PageUpdateBehavior), - indexOperation: (_ currentIndex: PageIndex, _ newIndex: PageIndex) -> Void) { + 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 } @@ -49,7 +50,7 @@ internal extension PageboyViewController { animated: false, async: false, force: false, - completion: nil) + completion: completion) self.currentIndex = newIndex return } @@ -64,7 +65,7 @@ internal extension PageboyViewController { animated: false, async: true, force: false, - completion: nil) + completion: completion) }) } else { // update is happening on some other page. indexOperation(currentIndex, newIndex) @@ -72,6 +73,7 @@ internal extension PageboyViewController { // 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 } @@ -83,6 +85,7 @@ internal extension PageboyViewController { case .insert: guard let currentViewController = currentViewController else { + completion?(true) return } newViewController = currentViewController @@ -93,9 +96,11 @@ internal extension PageboyViewController { 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: update.behavior) + completion?(true) } } } diff --git a/Sources/Pageboy/PageboyViewController.swift b/Sources/Pageboy/PageboyViewController.swift index 3e38db6b..664565f3 100755 --- a/Sources/Pageboy/PageboyViewController.swift +++ b/Sources/Pageboy/PageboyViewController.swift @@ -288,15 +288,17 @@ open class PageboyViewController: UIViewController { viewControllerCount = newPageCount viewControllerIndexMap.removeAll() - - performUpdates(for: index, - viewController: newViewController, - update: (operation: .insert, behavior: updateBehavior), - indexOperation: { (index, newIndex) in - + + 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 }) }) } @@ -329,14 +331,16 @@ open class PageboyViewController: UIViewController { viewControllerCount = newPageCount viewControllerIndexMap.removeAll() - performUpdates(for: newIndex, - viewController: newViewController, + 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 }) }) } diff --git a/Sources/Pageboy/Utilities/Extensions/UIScrollView+ScrollActivity.swift b/Sources/Pageboy/Utilities/Extensions/UIScrollView+Interaction.swift similarity index 67% rename from Sources/Pageboy/Utilities/Extensions/UIScrollView+ScrollActivity.swift rename to Sources/Pageboy/Utilities/Extensions/UIScrollView+Interaction.swift index 2938a468..52c6e61e 100644 --- a/Sources/Pageboy/Utilities/Extensions/UIScrollView+ScrollActivity.swift +++ b/Sources/Pageboy/Utilities/Extensions/UIScrollView+Interaction.swift @@ -1,5 +1,5 @@ // -// UIScrollView+ScrollActivity.swift +// UIScrollView+Interaction.swift // Pageboy // // Created by Merrick Sapsford on 23/01/2018. @@ -14,4 +14,9 @@ internal extension UIScrollView { var isProbablyActiveInScroll: Bool { return isTracking || isDecelerating } + + func cancelTouches() { + panGestureRecognizer.isEnabled = false + panGestureRecognizer.isEnabled = true + } }