Size class switching fixes for new tab/side bar
This commit is contained in:
parent
3d9a1086b6
commit
67e9c1245e
|
@ -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 */,
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,10 +264,8 @@ 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
|
||||||
// this, but the rest of the time it's up to you.
|
// this, but the rest of the time it's up to you.
|
||||||
|
@ -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()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue