2019-09-14 12:04:06 -04:00
|
|
|
//
|
|
|
|
// SegmentedPageViewController.swift
|
|
|
|
// Tusker
|
|
|
|
//
|
|
|
|
// Created by Shadowfacts on 9/13/19.
|
|
|
|
// Copyright © 2019 Shadowfacts. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
import UIKit
|
|
|
|
|
2022-12-12 20:57:38 -05:00
|
|
|
class SegmentedPageViewController<Page: Hashable>: UIPageViewController, UIPageViewControllerDelegate, TabbedPageViewController {
|
2019-09-14 12:04:06 -04:00
|
|
|
|
2022-12-12 20:57:38 -05:00
|
|
|
let pages: [Page]
|
2019-09-14 12:04:06 -04:00
|
|
|
let pageControllers: [UIViewController]
|
|
|
|
|
2022-12-12 20:57:38 -05:00
|
|
|
private var initialPage: Page
|
|
|
|
private var currentPage: Page
|
|
|
|
var currentIndex: Int {
|
|
|
|
pages.firstIndex(of: currentPage)!
|
|
|
|
}
|
2019-09-14 12:04:06 -04:00
|
|
|
|
2022-12-12 20:57:38 -05:00
|
|
|
let segmentedControl = ScrollingSegmentedControl<Page>()
|
2019-09-14 12:04:06 -04:00
|
|
|
|
2022-12-12 20:57:38 -05:00
|
|
|
init(pages: [(Page, String, UIViewController)]) {
|
|
|
|
precondition(!pages.isEmpty)
|
|
|
|
|
|
|
|
self.pages = pages.map(\.0)
|
|
|
|
self.pageControllers = pages.map(\.2)
|
2022-07-11 15:07:11 -04:00
|
|
|
|
2022-12-12 20:57:38 -05:00
|
|
|
initialPage = self.pages.first!
|
|
|
|
currentPage = self.pages.first!
|
2019-09-14 12:04:06 -04:00
|
|
|
|
|
|
|
super.init(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
|
2022-06-09 23:33:31 -04:00
|
|
|
|
|
|
|
// this needs to happen in init because EnhancedNavigationViewController expects to be able to look at the titleView
|
|
|
|
// before the view has necessarily loaded
|
2022-12-12 20:57:38 -05:00
|
|
|
segmentedControl.options = pages.map {
|
|
|
|
.init(value: $0.0, name: $0.1)
|
|
|
|
}
|
|
|
|
segmentedControl.didSelectOption = { [unowned self] option in
|
|
|
|
if let option {
|
|
|
|
self.selectPage(option, animated: true)
|
|
|
|
}
|
|
|
|
}
|
2022-12-12 22:47:16 -05:00
|
|
|
// TODO: the custom segmented control isn't treated as a group and I have no idea how to change that
|
2022-12-05 16:25:16 -05:00
|
|
|
// the segemented control itself is only focusable when VoiceOver is in Group navigation mode,
|
|
|
|
// so make it clear that to switch tabs the user needs to enter the group
|
|
|
|
segmentedControl.accessibilityHint = "Enter group to select timeline"
|
2022-12-12 20:57:38 -05:00
|
|
|
segmentedControl.setSelectedOption(segmentedControl.options.first!.value, animated: false)
|
2022-06-09 23:33:31 -04:00
|
|
|
navigationItem.titleView = segmentedControl
|
2019-09-14 12:04:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
required init?(coder: NSCoder) {
|
|
|
|
fatalError("init(coder:) has not been implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
override func viewDidLoad() {
|
|
|
|
super.viewDidLoad()
|
|
|
|
|
|
|
|
view.backgroundColor = .systemBackground
|
|
|
|
|
2022-12-12 20:57:38 -05:00
|
|
|
selectPage(initialPage, animated: false)
|
2020-11-14 22:26:02 -05:00
|
|
|
|
2020-11-14 22:28:52 -05:00
|
|
|
addKeyCommand(MenuController.prevSubTabCommand)
|
|
|
|
addKeyCommand(MenuController.nextSubTabCommand)
|
2021-06-10 10:55:09 -04:00
|
|
|
|
|
|
|
// disable the transparent nav bar because it gets messy with multiple pages at different scroll positions
|
|
|
|
if let nav = navigationController {
|
|
|
|
let appearance = UINavigationBarAppearance()
|
|
|
|
appearance.configureWithDefaultBackground()
|
|
|
|
nav.navigationBar.scrollEdgeAppearance = appearance
|
|
|
|
}
|
2019-09-14 12:04:06 -04:00
|
|
|
}
|
|
|
|
|
2022-12-12 20:57:38 -05:00
|
|
|
func selectPage(_ page: Page, animated: Bool) {
|
|
|
|
guard pages.contains(page) else {
|
|
|
|
fatalError("invalid page \(page) that is not in SegmentedPageViewController.pages")
|
|
|
|
}
|
2022-11-28 16:33:19 -05:00
|
|
|
guard isViewLoaded else {
|
2022-12-12 20:57:38 -05:00
|
|
|
initialPage = page
|
2022-11-28 16:33:19 -05:00
|
|
|
return
|
|
|
|
}
|
2022-12-12 20:57:38 -05:00
|
|
|
let prevIndex = currentIndex
|
|
|
|
currentPage = page
|
|
|
|
let index = pages.firstIndex(of: page)!
|
|
|
|
let newController = pageControllers[index]
|
|
|
|
|
|
|
|
let direction: UIPageViewController.NavigationDirection = index - prevIndex > 0 ? .forward : .reverse
|
|
|
|
setViewControllers([newController], direction: direction, animated: animated)
|
|
|
|
navigationItem.title = newController.title
|
|
|
|
|
|
|
|
segmentedControl.setSelectedOption(page, animated: animated)
|
2019-09-14 12:04:06 -04:00
|
|
|
}
|
|
|
|
|
2022-12-12 20:57:38 -05:00
|
|
|
// MARK: TabbedPageViewController
|
|
|
|
|
|
|
|
func selectNextPage() {
|
|
|
|
guard currentIndex < pageControllers.count - 1 else { return }
|
|
|
|
selectPage(pages[currentIndex + 1], animated: true)
|
2019-09-14 12:04:06 -04:00
|
|
|
}
|
2022-12-12 20:57:38 -05:00
|
|
|
|
|
|
|
func selectPrevPage() {
|
|
|
|
guard currentIndex > 0 else { return }
|
|
|
|
selectPage(pages[currentIndex - 1], animated: true)
|
2019-09-14 12:04:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2020-07-03 19:36:48 -04:00
|
|
|
|
|
|
|
extension SegmentedPageViewController: TabBarScrollableViewController {
|
|
|
|
func tabBarScrollToTop() {
|
|
|
|
if let scrollableVC = pageControllers[currentIndex] as? TabBarScrollableViewController {
|
|
|
|
scrollableVC.tabBarScrollToTop()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-10-26 22:55:58 -04:00
|
|
|
|
|
|
|
extension SegmentedPageViewController: BackgroundableViewController {
|
|
|
|
func sceneDidEnterBackground() {
|
|
|
|
if let current = pageControllers[currentIndex] as? BackgroundableViewController {
|
|
|
|
current.sceneDidEnterBackground()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-11-01 20:49:07 -04:00
|
|
|
|
|
|
|
extension SegmentedPageViewController: StatusBarTappableViewController {
|
|
|
|
func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult {
|
|
|
|
if let current = pageControllers[currentIndex] as? StatusBarTappableViewController {
|
|
|
|
return current.handleStatusBarTapped(xPosition: xPosition)
|
|
|
|
}
|
|
|
|
return .continue
|
|
|
|
}
|
|
|
|
}
|