Size class switching fixes for new tab/side bar

This commit is contained in:
Shadowfacts 2024-08-20 22:39:40 -04:00
parent 3d9a1086b6
commit 67e9c1245e
4 changed files with 258 additions and 50 deletions

View File

@ -132,6 +132,7 @@
D64A50462C739DC0009D7193 /* NewMainTabBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64A50452C739DC0009D7193 /* NewMainTabBarViewController.swift */; }; D64A50462C739DC0009D7193 /* NewMainTabBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64A50452C739DC0009D7193 /* NewMainTabBarViewController.swift */; };
D64A50482C739DEA009D7193 /* BaseMainTabBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64A50472C739DEA009D7193 /* BaseMainTabBarViewController.swift */; }; D64A50482C739DEA009D7193 /* BaseMainTabBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64A50472C739DEA009D7193 /* BaseMainTabBarViewController.swift */; };
D64A50BC2C74F8F4009D7193 /* FindInstanceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64A50BB2C74F8F4009D7193 /* FindInstanceViewController.swift */; }; D64A50BC2C74F8F4009D7193 /* FindInstanceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64A50BB2C74F8F4009D7193 /* FindInstanceViewController.swift */; };
D64A50BE2C752247009D7193 /* AdaptableNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64A50BD2C752247009D7193 /* AdaptableNavigationController.swift */; };
D64AAE9126C80DC600FC57FB /* ToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64AAE9026C80DC600FC57FB /* ToastView.swift */; }; D64AAE9126C80DC600FC57FB /* ToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64AAE9026C80DC600FC57FB /* ToastView.swift */; };
D64AAE9526C88C5000FC57FB /* ToastableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64AAE9426C88C5000FC57FB /* ToastableViewController.swift */; }; D64AAE9526C88C5000FC57FB /* ToastableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64AAE9426C88C5000FC57FB /* ToastableViewController.swift */; };
D64AAE9726C88DC400FC57FB /* ToastConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64AAE9626C88DC400FC57FB /* ToastConfiguration.swift */; }; D64AAE9726C88DC400FC57FB /* ToastConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64AAE9626C88DC400FC57FB /* ToastConfiguration.swift */; };
@ -563,6 +564,7 @@
D64A50452C739DC0009D7193 /* NewMainTabBarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewMainTabBarViewController.swift; sourceTree = "<group>"; }; D64A50452C739DC0009D7193 /* NewMainTabBarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewMainTabBarViewController.swift; sourceTree = "<group>"; };
D64A50472C739DEA009D7193 /* BaseMainTabBarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseMainTabBarViewController.swift; sourceTree = "<group>"; }; D64A50472C739DEA009D7193 /* BaseMainTabBarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseMainTabBarViewController.swift; sourceTree = "<group>"; };
D64A50BB2C74F8F4009D7193 /* FindInstanceViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FindInstanceViewController.swift; sourceTree = "<group>"; }; D64A50BB2C74F8F4009D7193 /* FindInstanceViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FindInstanceViewController.swift; sourceTree = "<group>"; };
D64A50BD2C752247009D7193 /* AdaptableNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdaptableNavigationController.swift; sourceTree = "<group>"; };
D64AAE9026C80DC600FC57FB /* ToastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastView.swift; sourceTree = "<group>"; }; D64AAE9026C80DC600FC57FB /* ToastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastView.swift; sourceTree = "<group>"; };
D64AAE9426C88C5000FC57FB /* ToastableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastableViewController.swift; sourceTree = "<group>"; }; D64AAE9426C88C5000FC57FB /* ToastableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastableViewController.swift; sourceTree = "<group>"; };
D64AAE9626C88DC400FC57FB /* ToastConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastConfiguration.swift; sourceTree = "<group>"; }; D64AAE9626C88DC400FC57FB /* ToastConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastConfiguration.swift; sourceTree = "<group>"; };
@ -1556,6 +1558,7 @@
D6B8DB332182A59300424AF7 /* UIAlertController+Visibility.swift */, D6B8DB332182A59300424AF7 /* UIAlertController+Visibility.swift */,
D6C693FD2162FEEA007D6A6D /* UIViewController+Children.swift */, D6C693FD2162FEEA007D6A6D /* UIViewController+Children.swift */,
D61F759129365C6C00C0B37F /* CollectionViewController.swift */, D61F759129365C6C00C0B37F /* CollectionViewController.swift */,
D64A50BD2C752247009D7193 /* AdaptableNavigationController.swift */,
); );
path = Utilities; path = Utilities;
sourceTree = "<group>"; sourceTree = "<group>";
@ -2341,6 +2344,7 @@
D6C693FE2162FEEA007D6A6D /* UIViewController+Children.swift in Sources */, D6C693FE2162FEEA007D6A6D /* UIViewController+Children.swift in Sources */,
D61A45EA28DF51EE002BE511 /* TimelineLikeCollectionViewController.swift in Sources */, D61A45EA28DF51EE002BE511 /* TimelineLikeCollectionViewController.swift in Sources */,
D64D0AB12128D9AE005A6F37 /* OnboardingViewController.swift in Sources */, D64D0AB12128D9AE005A6F37 /* OnboardingViewController.swift in Sources */,
D64A50BE2C752247009D7193 /* AdaptableNavigationController.swift in Sources */,
D6934F302BA7AF91002B1C8D /* ImageGalleryContentViewController.swift in Sources */, D6934F302BA7AF91002B1C8D /* ImageGalleryContentViewController.swift in Sources */,
D6934F3E2BAA19D5002B1C8D /* ImageActivityItemSource.swift in Sources */, D6934F3E2BAA19D5002B1C8D /* ImageActivityItemSource.swift in Sources */,
D67C1795266D57D10070F250 /* FastAccountSwitcherIndicatorView.swift in Sources */, D67C1795266D57D10070F250 /* FastAccountSwitcherIndicatorView.swift in Sources */,

View File

@ -71,11 +71,12 @@ class BaseMainTabBarViewController: UITabBarController {
private func repositionFastSwitcherIndicator() { private func repositionFastSwitcherIndicator() {
guard let myProfileButton = findMyProfileTabBarButton(), guard let myProfileButton = findMyProfileTabBarButton(),
myProfileButton.window != nil else { myProfileButton.window != nil,
let fastSwitcherIndicator else {
fastSwitcherIndicator?.isHidden = true fastSwitcherIndicator?.isHidden = true
return return
} }
fastSwitcherIndicator?.isHidden = false fastSwitcherIndicator.isHidden = false
NSLayoutConstraint.deactivate(fastSwitcherConstraints) NSLayoutConstraint.deactivate(fastSwitcherConstraints)
let isPortrait = view.bounds.width < view.bounds.height let isPortrait = view.bounds.width < view.bounds.height
if traitCollection.horizontalSizeClass == .compact && isPortrait { if traitCollection.horizontalSizeClass == .compact && isPortrait {
@ -156,7 +157,7 @@ extension BaseMainTabBarViewController: StateRestorableViewController {
let compose = presentedNav.viewControllers.first as? ComposeHostingController { let compose = presentedNav.viewControllers.first as? ComposeHostingController {
let draft = compose.controller.draft let draft = compose.controller.draft
activity = UserActivityManager.editDraftActivity(id: draft.id, accountID: draft.accountID) activity = UserActivityManager.editDraftActivity(id: draft.id, accountID: draft.accountID)
} else if let vc = (selectedViewController as! UINavigationController).topViewController as? StateRestorableViewController { } else if let vc = (selectedViewController as? any NavigationControllerProtocol)?.topViewController as? StateRestorableViewController {
activity = vc.stateRestorationActivity() activity = vc.stateRestorationActivity()
} }
if activity == nil { if activity == nil {

View File

@ -15,11 +15,19 @@ class NewMainTabBarViewController: BaseMainTabBarViewController {
private let composePlaceholder = UIViewController() private let composePlaceholder = UIViewController()
private var homeTab: UITab!
private var notificationsTab: UITab!
private var composeTab: UITab!
private var exploreTab: UITab!
private var bookmarksTab: UITab!
private var favoritesTab: UITab!
private var myProfileTab: UITab!
private var listsGroup: UITabGroup! private var listsGroup: UITabGroup!
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
private var navigationStacks = [String: [UIViewController]]() private var navigationStacks = [String: [UIViewController]]()
private var isCompact: Bool?
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
@ -34,19 +42,19 @@ class NewMainTabBarViewController: BaseMainTabBarViewController {
self.makeViewController(for: tab) self.makeViewController(for: tab)
} }
let homeTab = UITab(title: "Home", image: UIImage(systemName: "house"), identifier: Tab.home.rawValue, viewControllerProvider: viewControllerProvider) homeTab = UITab(title: "Home", image: UIImage(systemName: "house"), identifier: Tab.home.rawValue, viewControllerProvider: viewControllerProvider)
let notificationsTab = UITab(title: "Notifications", image: UIImage(systemName: "bell"), identifier: Tab.notifications.rawValue, viewControllerProvider: viewControllerProvider) notificationsTab = UITab(title: "Notifications", image: UIImage(systemName: "bell"), identifier: Tab.notifications.rawValue, viewControllerProvider: viewControllerProvider)
let composeTab = UITab(title: "Compose", image: UIImage(systemName: "pencil"), identifier: Tab.compose.rawValue, viewControllerProvider: viewControllerProvider) composeTab = UITab(title: "Compose", image: UIImage(systemName: "pencil"), identifier: Tab.compose.rawValue, viewControllerProvider: viewControllerProvider)
let exploreTab = UITab(title: "Explore", image: UIImage(systemName: "magnifyingglass"), identifier: Tab.explore.rawValue, viewControllerProvider: viewControllerProvider) exploreTab = UITab(title: "Explore", image: UIImage(systemName: "magnifyingglass"), identifier: Tab.explore.rawValue, viewControllerProvider: viewControllerProvider)
let bookmarksTab = UITab(title: "Bookmarks", image: UIImage(systemName: "bookmark"), identifier: Tab.bookmarks.rawValue, viewControllerProvider: viewControllerProvider) bookmarksTab = UITab(title: "Bookmarks", image: UIImage(systemName: "bookmark"), identifier: Tab.bookmarks.rawValue, viewControllerProvider: viewControllerProvider)
bookmarksTab.preferredPlacement = .optional bookmarksTab.preferredPlacement = .optional
let favoritesTab = UITab(title: "Favorites", image: UIImage(systemName: "star"), identifier: Tab.favorites.rawValue, viewControllerProvider: viewControllerProvider) favoritesTab = UITab(title: "Favorites", image: UIImage(systemName: "star"), identifier: Tab.favorites.rawValue, viewControllerProvider: viewControllerProvider)
favoritesTab.preferredPlacement = .optional favoritesTab.preferredPlacement = .optional
let myProfileTab = UITab(title: "My Profile", image: UIImage(systemName: "person"), identifier: Tab.myProfile.rawValue, viewControllerProvider: viewControllerProvider) myProfileTab = UITab(title: "My Profile", image: UIImage(systemName: "person"), identifier: Tab.myProfile.rawValue, viewControllerProvider: viewControllerProvider)
listsGroup = UITabGroup(title: "Lists", image: nil, identifier: Tab.lists.rawValue, children: []) { _ in listsGroup = UITabGroup(title: "Lists", image: nil, identifier: Tab.lists.rawValue, children: []) { _ in
// this closure is necessary to prevent UIKit from crashing (FB14860961) // this closure is necessary to prevent UIKit from crashing (FB14860961)
return MultiColumnNavigationController() return AdaptableNavigationController()
} }
listsGroup.preferredPlacement = .sidebarOnly listsGroup.preferredPlacement = .sidebarOnly
listsGroup.sidebarActions = [ listsGroup.sidebarActions = [
@ -65,6 +73,72 @@ class NewMainTabBarViewController: BaseMainTabBarViewController {
myProfileTab, myProfileTab,
] ]
} else { } else {
self.updatePadTabs()
registerForTraitChanges([UITraitHorizontalSizeClass.self]) { (self: NewMainTabBarViewController, previousTraitCollection) in
self.updatePadTabs()
let vcToUpdate = self.selectedTab!.parent?.viewController ?? self.selectedTab!.viewController!
self.updateViewControllerSafeAreaInsets(vcToUpdate)
}
}
setupFastAccountSwitcher()
mastodonController.$lists
.sink { [unowned self] in self.reloadLists($0) }
.store(in: &cancellables)
}
private func updatePadTabs() {
let wasCompact = isCompact
if self.traitCollection.horizontalSizeClass == .compact {
isCompact = true
var exploreNavStack: [UIViewController]? = nil
if selectedTab?.parent == listsGroup {
let nav = listsGroup.viewController as! any NavigationControllerProtocol
exploreNavStack = nav.viewControllers
nav.viewControllers = []
}
self.tabs = [
homeTab,
notificationsTab,
composeTab,
exploreTab,
myProfileTab,
]
if let exploreNavStack {
selectedTab = exploreTab
let nav = exploreTab.viewController as! any NavigationControllerProtocol
nav.viewControllers = exploreNavStack
}
} else {
isCompact = false
var newTab: (UITab, [UIViewController])? = nil
if wasCompact == true,
selectedTab == exploreTab {
let nav = exploreTab.viewController as! any NavigationControllerProtocol
// skip over the ExploreViewController
if nav.viewControllers.count > 1 {
switch nav.viewControllers[1] {
case let listVC as ListTimelineViewController:
if let tab = listsGroup.tab(forIdentifier: "list:\(listVC.list.id)") {
newTab = (tab, Array(nav.viewControllers[1...]))
nav.viewControllers = [
nav.viewControllers[0], // leave the ExploreVC in place
InlineTrendsViewController(mastodonController: mastodonController), // re-insert an InlineTrendsVC
]
}
default:
break
}
}
}
self.tabs = [ self.tabs = [
homeTab, homeTab,
notificationsTab, notificationsTab,
@ -75,13 +149,17 @@ class NewMainTabBarViewController: BaseMainTabBarViewController {
composeTab, composeTab,
listsGroup, listsGroup,
] ]
if let (tab, navStack) = newTab {
let nav = tab.parent!.viewController as! any NavigationControllerProtocol
nav.viewControllers = navStack
// Setting the tab now seems to be clobbered by the UITabBarController itself updating in response
// to the size class change. So wait until it finishes to do so.
DispatchQueue.main.async {
self.selectedTab = tab
}
}
} }
setupFastAccountSwitcher()
mastodonController.$lists
.sink { [unowned self] in self.reloadLists($0) }
.store(in: &cancellables)
} }
private func makeViewController(for tab: UITab) -> UIViewController { private func makeViewController(for tab: UITab) -> UIViewController {
@ -100,7 +178,11 @@ class NewMainTabBarViewController: BaseMainTabBarViewController {
if UIDevice.current.userInterfaceIdiom == .phone { if UIDevice.current.userInterfaceIdiom == .phone {
root = ExploreViewController(mastodonController: mastodonController) root = ExploreViewController(mastodonController: mastodonController)
} else { } else {
root = InlineTrendsViewController(mastodonController: mastodonController) let nav = AdaptableNavigationController(viewControllersToPrependInCompact: [
ExploreViewController(mastodonController: mastodonController)
])
nav.viewControllers = [InlineTrendsViewController(mastodonController: mastodonController)]
return nav
} }
case .bookmarks: case .bookmarks:
root = BookmarksViewController(mastodonController: mastodonController) root = BookmarksViewController(mastodonController: mastodonController)
@ -111,24 +193,11 @@ class NewMainTabBarViewController: BaseMainTabBarViewController {
case .lists: case .lists:
fatalError("unreachable") fatalError("unreachable")
} }
return NewMainTabBarViewController.embedInNavigationController(root) return embedInNavigationController(root)
} }
private static func embedInNavigationController(_ vc: UIViewController) -> UIViewController { private func embedInNavigationController(_ vc: UIViewController) -> UIViewController {
let nav: any NavigationControllerProtocol let nav = AdaptableNavigationController()
if UIDevice.current.userInterfaceIdiom == .phone {
nav = EnhancedNavigationViewController()
} else {
// TODO: need to figure out how to update the navigation controller if the pref changes
switch Preferences.shared.widescreenNavigationMode {
case .stack:
nav = EnhancedNavigationViewController()
case .splitScreen:
nav = SplitNavigationController()
case .multiColumn:
nav = MultiColumnNavigationController()
}
}
nav.viewControllers = [vc] nav.viewControllers = [vc]
return nav return nav
} }
@ -136,7 +205,7 @@ class NewMainTabBarViewController: BaseMainTabBarViewController {
private func reloadLists(_ lists: [List]) { private func reloadLists(_ lists: [List]) {
listsGroup.children = lists.map { list in listsGroup.children = lists.map { list in
UITab(title: list.title, image: UIImage(systemName: "list.bullet"), identifier: "list:\(list.id)") { [unowned self] _ in UITab(title: list.title, image: UIImage(systemName: "list.bullet"), identifier: "list:\(list.id)") { [unowned self] _ in
NewMainTabBarViewController.embedInNavigationController(ListTimelineViewController(for: list, mastodonController: self.mastodonController)) return ListTimelineViewController(for: list, mastodonController: self.mastodonController)
} }
} }
} }
@ -145,7 +214,10 @@ class NewMainTabBarViewController: BaseMainTabBarViewController {
compose(editing: nil) compose(editing: nil)
} }
fileprivate func updateViewControllerSafeAreaInsets(_ vc: MultiColumnNavigationController) { fileprivate func updateViewControllerSafeAreaInsets(_ vc: UIViewController) {
guard vc is MultiColumnNavigationController || (vc as? AdaptableNavigationController)?.current is MultiColumnNavigationController else {
return
}
// When in sidebar mode, for multi column mode, don't leave an inset for the floating tab bar, because it leaves a massive gap. // When in sidebar mode, for multi column mode, don't leave an inset for the floating tab bar, because it leaves a massive gap.
// The floating tab bar seems to always be 88pt tall, regardless of, e.g., Dynamic Type size. // The floating tab bar seems to always be 88pt tall, regardless of, e.g., Dynamic Type size.
vc.additionalSafeAreaInsets = UIEdgeInsets(top: sidebar.isHidden ? 0 : -88, left: 0, bottom: 0, right: 0) vc.additionalSafeAreaInsets = UIEdgeInsets(top: sidebar.isHidden ? 0 : -88, left: 0, bottom: 0, right: 0)
@ -192,9 +264,7 @@ extension NewMainTabBarViewController: UITabBarControllerDelegate {
} }
func tabBarController(_ tabBarController: UITabBarController, didSelectTab newTab: UITab, previousTab: UITab?) { func tabBarController(_ tabBarController: UITabBarController, didSelectTab newTab: UITab, previousTab: UITab?) {
if let vc = newTab.viewController as? MultiColumnNavigationController { self.updateViewControllerSafeAreaInsets(newTab.viewController!)
self.updateViewControllerSafeAreaInsets(vc)
}
// All tabs in a tab group deliberately share the same view controller, so we have to do this ourselves. // All tabs in a tab group deliberately share the same view controller, so we have to do this ourselves.
// I think this is pretty unfortunate API design--half the time, the tab bar controller takes care of // I think this is pretty unfortunate API design--half the time, the tab bar controller takes care of
@ -207,9 +277,7 @@ extension NewMainTabBarViewController: UITabBarControllerDelegate {
if let group = newTab.parent, if let group = newTab.parent,
group.identifier == Tab.lists.rawValue, group.identifier == Tab.lists.rawValue,
let nav = group.viewController as? any NavigationControllerProtocol { let nav = group.viewController as? any NavigationControllerProtocol {
if let multiColumn = nav as? MultiColumnNavigationController { updateViewControllerSafeAreaInsets(nav)
updateViewControllerSafeAreaInsets(multiColumn)
}
if let previousTab { if let previousTab {
navigationStacks[previousTab.identifier] = nav.viewControllers navigationStacks[previousTab.identifier] = nav.viewControllers
@ -217,8 +285,8 @@ extension NewMainTabBarViewController: UITabBarControllerDelegate {
if let existing = navigationStacks[newTab.identifier] { if let existing = navigationStacks[newTab.identifier] {
nav.viewControllers = existing nav.viewControllers = existing
} else if let newNav = newTab.viewController as? any NavigationControllerProtocol { } else if let newVC = newTab.viewController {
nav.viewControllers = newNav.viewControllers nav.viewControllers = [newVC]
} else { } else {
fatalError("unreachable") fatalError("unreachable")
} }
@ -229,11 +297,10 @@ extension NewMainTabBarViewController: UITabBarControllerDelegate {
@available(iOS 18.0, *) @available(iOS 18.0, *)
extension NewMainTabBarViewController: UITabBarController.Sidebar.Delegate { extension NewMainTabBarViewController: UITabBarController.Sidebar.Delegate {
func tabBarController(_ tabBarController: UITabBarController, sidebarVisibilityWillChange sidebar: UITabBarController.Sidebar, animator: any UITabBarController.Sidebar.Animating) { func tabBarController(_ tabBarController: UITabBarController, sidebarVisibilityWillChange sidebar: UITabBarController.Sidebar, animator: any UITabBarController.Sidebar.Animating) {
if let vc = selectedViewController as? MultiColumnNavigationController { let vc = selectedTab!.parent?.viewController ?? selectedTab!.viewController!
animator.addAnimations { animator.addAnimations {
self.updateViewControllerSafeAreaInsets(vc) self.updateViewControllerSafeAreaInsets(vc)
vc.view.layoutIfNeeded() vc.view.layoutIfNeeded()
}
} }
} }
} }

View File

@ -0,0 +1,136 @@
//
// AdaptableNavigationController.swift
// Tusker
//
// Created by Shadowfacts on 8/20/24.
// Copyright © 2024 Shadowfacts. All rights reserved.
//
import UIKit
import Combine
@available(iOS 17.0, *)
class AdaptableNavigationController: UIViewController {
private let viewControllersToPrependInCompact: [UIViewController]
private var initialViewControllers: [UIViewController] = []
private lazy var regular = makeRegularNavigationController()
private lazy var compact = makeCompactNavigationController()
private var _current: (any NavigationControllerProtocol)?
var current: any NavigationControllerProtocol {
traitCollection.horizontalSizeClass == .regular ? regular : compact
}
init(viewControllersToPrependInCompact: [UIViewController] = []) {
self.viewControllersToPrependInCompact = viewControllersToPrependInCompact
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
updateNavigationController()
registerForTraitChanges([UITraitHorizontalSizeClass.self]) { (self: AdaptableNavigationController, previousTraitCollection) in
self.updateNavigationController()
}
}
private func updateNavigationController() {
let isTransferring: Bool
var stack: [UIViewController]
if let _current {
_current.removeViewAndController()
stack = _current.viewControllers
isTransferring = true
} else {
stack = initialViewControllers
initialViewControllers = []
isTransferring = false
}
if traitCollection.horizontalSizeClass == .regular {
if isTransferring {
stack.removeFirst(viewControllersToPrependInCompact.count)
}
} else {
stack.insert(contentsOf: viewControllersToPrependInCompact, at: 0)
}
_current = current
current.viewControllers = stack
addChild(current)
current.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(current.view)
NSLayoutConstraint.activate([
current.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
current.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
current.view.topAnchor.constraint(equalTo: view.topAnchor),
current.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
current.didMove(toParent: self)
}
private func makeRegularNavigationController() -> any NavigationControllerProtocol {
// TODO: need to figure out how to update the navigation controller if the pref changes
switch Preferences.shared.widescreenNavigationMode {
case .stack:
return EnhancedNavigationViewController()
case .splitScreen:
return SplitNavigationController()
case .multiColumn:
return MultiColumnNavigationController()
}
}
private func makeCompactNavigationController() -> any NavigationControllerProtocol {
EnhancedNavigationViewController()
}
}
@available(iOS 17.0, *)
extension AdaptableNavigationController: NavigationControllerProtocol {
var viewControllers: [UIViewController] {
get {
_current?.viewControllers ?? initialViewControllers
}
set {
if let _current {
_current.viewControllers = newValue
} else {
initialViewControllers = newValue
}
}
}
var topViewController: UIViewController? {
if let _current {
return _current.topViewController
} else {
return initialViewControllers.last
}
}
func popToRootViewController(animated: Bool) -> [UIViewController]? {
if let _current {
return _current.popToRootViewController(animated: animated)
} else {
defer { initialViewControllers = [] }
return initialViewControllers
}
}
func pushViewController(_ vc: UIViewController, animated: Bool) {
if let _current {
_current.pushViewController(vc, animated: animated)
} else {
initialViewControllers.append(vc)
}
}
}