Don't use UIPageViewController for SegmentedPageViewController

This commit is contained in:
Shadowfacts 2023-01-16 16:27:32 -05:00
parent baa9dfe0f1
commit e4f3735c9f
1 changed files with 44 additions and 11 deletions

View File

@ -12,8 +12,8 @@ protocol SegmentedPageViewControllerPage: Hashable {
var segmentedControlTitle: String { get } var segmentedControlTitle: String { get }
} }
class SegmentedPageViewController<Page: SegmentedPageViewControllerPage>: UIPageViewController, UIPageViewControllerDelegate, TabbedPageViewController { class SegmentedPageViewController<Page: SegmentedPageViewControllerPage>: UIViewController, UIPageViewControllerDelegate, TabbedPageViewController {
private(set) var pages: [Page]! private(set) var pages: [Page]!
private let pageProvider: (Page) -> UIViewController private let pageProvider: (Page) -> UIViewController
private var pageControllers = [Page: UIViewController]() private var pageControllers = [Page: UIViewController]()
@ -23,12 +23,10 @@ class SegmentedPageViewController<Page: SegmentedPageViewControllerPage>: UIPage
var currentIndex: Int! { var currentIndex: Int! {
pages.firstIndex(of: currentPage) pages.firstIndex(of: currentPage)
} }
var currentViewController: UIViewController! { var currentViewController: UIViewController!
viewControllers?.first
}
let segmentedControl = ScrollingSegmentedControl<Page>() let segmentedControl = ScrollingSegmentedControl<Page>()
init(pages: [Page], pageProvider: @escaping (Page) -> UIViewController) { init(pages: [Page], pageProvider: @escaping (Page) -> UIViewController) {
precondition(!pages.isEmpty) precondition(!pages.isEmpty)
@ -37,7 +35,7 @@ class SegmentedPageViewController<Page: SegmentedPageViewControllerPage>: UIPage
initialPage = pages.first! initialPage = pages.first!
currentPage = pages.first! currentPage = pages.first!
super.init(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil) super.init(nibName: nil, bundle: nil)
setPages(pages, animated: false) setPages(pages, animated: false)
@ -104,12 +102,12 @@ class SegmentedPageViewController<Page: SegmentedPageViewControllerPage>: UIPage
initialPage = page initialPage = page
return return
} }
let direction: UIPageViewController.NavigationDirection let direction: AnimationMode
if let prevIndex = currentIndex { if let prevIndex = currentIndex {
let index = pages.firstIndex(of: page)! let index = pages.firstIndex(of: page)!
direction = index - prevIndex > 0 ? .forward : .reverse direction = index - prevIndex > 0 ? .forward : .reverse
} else { } else {
direction = .forward direction = .none
} }
currentPage = page currentPage = page
@ -121,12 +119,40 @@ class SegmentedPageViewController<Page: SegmentedPageViewControllerPage>: UIPage
pageControllers[page] = newController pageControllers[page] = newController
} }
setViewControllers([newController], direction: direction, animated: animated) setViewController(newController, animated: animated ? direction : .none)
navigationItem.title = newController.title navigationItem.title = newController.title
segmentedControl.setSelectedOption(page, animated: animated) segmentedControl.setSelectedOption(page, animated: animated)
} }
private func setViewController(_ newViewController: UIViewController, animated: AnimationMode) {
guard let currentViewController,
animated != .none else {
currentViewController?.removeViewAndController()
newViewController.view.translatesAutoresizingMaskIntoConstraints = false
embedChild(newViewController)
self.currentViewController = newViewController
return
}
guard currentViewController !== newViewController else {
return
}
self.currentViewController = newViewController
newViewController.view.translatesAutoresizingMaskIntoConstraints = false
embedChild(newViewController)
let direction: CGFloat = animated == .forward ? 1 : -1
newViewController.view.transform = CGAffineTransform(translationX: direction * view.bounds.width, y: 0)
let animator = UIViewPropertyAnimator(duration: 0.5, timingParameters: UISpringTimingParameters(dampingRatio: 1, initialVelocity: .zero))
animator.addAnimations {
newViewController.view.transform = .identity
currentViewController.view.transform = CGAffineTransform(translationX: -1 * direction * self.view.bounds.width, y: 0)
}
animator.addCompletion { _ in
currentViewController.removeViewAndController()
}
animator.startAnimation()
}
// MARK: TabbedPageViewController // MARK: TabbedPageViewController
func selectNextPage() { func selectNextPage() {
@ -138,7 +164,14 @@ class SegmentedPageViewController<Page: SegmentedPageViewControllerPage>: UIPage
guard currentIndex > 0 else { return } guard currentIndex > 0 else { return }
selectPage(pages[currentIndex - 1], animated: true) selectPage(pages[currentIndex - 1], animated: true)
} }
}
extension SegmentedPageViewController {
enum AnimationMode: Equatable {
case none
case forward
case reverse
}
} }
extension SegmentedPageViewController: TabBarScrollableViewController { extension SegmentedPageViewController: TabBarScrollableViewController {