// // SegmentedPageViewController.swift // Tusker // // Created by Shadowfacts on 9/13/19. // Copyright © 2019 Shadowfacts. All rights reserved. // import UIKit class SegmentedPageViewController: UIPageViewController, UIPageViewControllerDelegate, TabbedPageViewController { let pages: [Page] let pageControllers: [UIViewController] private var initialPage: Page private var currentPage: Page var currentIndex: Int { pages.firstIndex(of: currentPage)! } let segmentedControl = ScrollingSegmentedControl() init(pages: [(Page, String, UIViewController)]) { precondition(!pages.isEmpty) self.pages = pages.map(\.0) self.pageControllers = pages.map(\.2) initialPage = self.pages.first! currentPage = self.pages.first! super.init(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil) // this needs to happen in init because EnhancedNavigationViewController expects to be able to look at the titleView // before the view has necessarily loaded 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) } } // TODO: double check this with the custom segmented control // 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" segmentedControl.setSelectedOption(segmentedControl.options.first!.value, animated: false) navigationItem.titleView = segmentedControl } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .systemBackground selectPage(initialPage, animated: false) addKeyCommand(MenuController.prevSubTabCommand) addKeyCommand(MenuController.nextSubTabCommand) // 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 } } func selectPage(_ page: Page, animated: Bool) { guard pages.contains(page) else { fatalError("invalid page \(page) that is not in SegmentedPageViewController.pages") } guard isViewLoaded else { initialPage = page return } 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) } // MARK: TabbedPageViewController func selectNextPage() { guard currentIndex < pageControllers.count - 1 else { return } selectPage(pages[currentIndex + 1], animated: true) } func selectPrevPage() { guard currentIndex > 0 else { return } selectPage(pages[currentIndex - 1], animated: true) } } extension SegmentedPageViewController: TabBarScrollableViewController { func tabBarScrollToTop() { if let scrollableVC = pageControllers[currentIndex] as? TabBarScrollableViewController { scrollableVC.tabBarScrollToTop() } } } extension SegmentedPageViewController: BackgroundableViewController { func sceneDidEnterBackground() { if let current = pageControllers[currentIndex] as? BackgroundableViewController { current.sceneDidEnterBackground() } } } extension SegmentedPageViewController: StatusBarTappableViewController { func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult { if let current = pageControllers[currentIndex] as? StatusBarTappableViewController { return current.handleStatusBarTapped(xPosition: xPosition) } return .continue } }